Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
Ftt_Common
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Ftt_Common
Commits
41b668f8
Commit
41b668f8
authored
Jun 24, 2026
by
段廷瑞
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 文本格式化功能
parent
2705cb1f
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1823 additions
and
0 deletions
+1823
-0
.gitignore
.gitignore
+1
-0
Tools.csproj
Tools/Tools.csproj
+9
-0
wTextFormatter.Designer.cs
Tools/UI/wTextFormatter.Designer.cs
+457
-0
wTextFormatter.cs
Tools/UI/wTextFormatter.cs
+1235
-0
wTextFormatter.resx
Tools/UI/wTextFormatter.resx
+120
-0
wExtend.cs
Tools/wExtend.cs
+1
-0
No files found.
.gitignore
View file @
41b668f8
...
@@ -62,3 +62,4 @@ bin
...
@@ -62,3 +62,4 @@ bin
/.claude/settings.local.json
/.claude/settings.local.json
/AppHome/ClaudeChat
/AppHome/ClaudeChat
/CLAUDE.md
/CLAUDE.md
*.zip
Tools/Tools.csproj
View file @
41b668f8
...
@@ -365,6 +365,12 @@
...
@@ -365,6 +365,12 @@
<Compile
Include=
"UI\wTaskBoard.Designer.cs"
>
<Compile
Include=
"UI\wTaskBoard.Designer.cs"
>
<DependentUpon>
wTaskBoard.cs
</DependentUpon>
<DependentUpon>
wTaskBoard.cs
</DependentUpon>
</Compile>
</Compile>
<Compile
Include=
"UI\wTextFormatter.cs"
>
<SubType>
Form
</SubType>
</Compile>
<Compile
Include=
"UI\wTextFormatter.Designer.cs"
>
<DependentUpon>
wTextFormatter.cs
</DependentUpon>
</Compile>
<Compile
Include=
"UI\wUser.cs"
>
<Compile
Include=
"UI\wUser.cs"
>
<SubType>
Form
</SubType>
<SubType>
Form
</SubType>
</Compile>
</Compile>
...
@@ -524,6 +530,9 @@
...
@@ -524,6 +530,9 @@
<EmbeddedResource
Include=
"UI\wTaskBoard.resx"
>
<EmbeddedResource
Include=
"UI\wTaskBoard.resx"
>
<DependentUpon>
wTaskBoard.cs
</DependentUpon>
<DependentUpon>
wTaskBoard.cs
</DependentUpon>
</EmbeddedResource>
</EmbeddedResource>
<EmbeddedResource
Include=
"UI\wTextFormatter.resx"
>
<DependentUpon>
wTextFormatter.cs
</DependentUpon>
</EmbeddedResource>
<EmbeddedResource
Include=
"UI\wUser.resx"
>
<EmbeddedResource
Include=
"UI\wUser.resx"
>
<DependentUpon>
wUser.cs
</DependentUpon>
<DependentUpon>
wUser.cs
</DependentUpon>
</EmbeddedResource>
</EmbeddedResource>
...
...
Tools/UI/wTextFormatter.Designer.cs
0 → 100644
View file @
41b668f8
namespace
Tools.UI
{
partial
class
wTextFormatter
{
/// <summary>
/// Required designer variable.
/// </summary>
private
System
.
ComponentModel
.
IContainer
components
=
null
;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected
override
void
Dispose
(
bool
disposing
)
{
if
(
disposing
&&
(
components
!=
null
))
{
components
.
Dispose
();
}
base
.
Dispose
(
disposing
);
}
#
region
Windows
Form
Designer
generated
code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private
void
InitializeComponent
()
{
this
.
tabControl
=
new
System
.
Windows
.
Forms
.
TabControl
();
this
.
tabSql
=
new
System
.
Windows
.
Forms
.
TabPage
();
this
.
tlpSql
=
new
System
.
Windows
.
Forms
.
TableLayoutPanel
();
this
.
panelSqlInputHeader
=
new
System
.
Windows
.
Forms
.
Panel
();
this
.
lblSqlInput
=
new
System
.
Windows
.
Forms
.
Label
();
this
.
lblDialect
=
new
System
.
Windows
.
Forms
.
Label
();
this
.
cbSqlDialect
=
new
System
.
Windows
.
Forms
.
ComboBox
();
this
.
btnFormatSql
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
tbSqlInput
=
new
System
.
Windows
.
Forms
.
TextBox
();
this
.
panelSqlResult
=
new
System
.
Windows
.
Forms
.
Panel
();
this
.
lblSqlOutput
=
new
System
.
Windows
.
Forms
.
Label
();
this
.
btnCopySql
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
tbSqlOutput
=
new
System
.
Windows
.
Forms
.
TextBox
();
this
.
tabJson
=
new
System
.
Windows
.
Forms
.
TabPage
();
this
.
tlpJson
=
new
System
.
Windows
.
Forms
.
TableLayoutPanel
();
this
.
panelJsonInputHeader
=
new
System
.
Windows
.
Forms
.
Panel
();
this
.
lblJsonInput
=
new
System
.
Windows
.
Forms
.
Label
();
this
.
btnUnescape
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
btnEscape
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
btnFormatJson
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
tbJsonInput
=
new
System
.
Windows
.
Forms
.
TextBox
();
this
.
panelJsonResult
=
new
System
.
Windows
.
Forms
.
Panel
();
this
.
lblJsonOutput
=
new
System
.
Windows
.
Forms
.
Label
();
this
.
btnExpand
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
btnCompress
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
btnCopyJson
=
new
System
.
Windows
.
Forms
.
Button
();
this
.
tbJsonOutput
=
new
System
.
Windows
.
Forms
.
TextBox
();
this
.
tabControl
.
SuspendLayout
();
this
.
tabSql
.
SuspendLayout
();
this
.
tlpSql
.
SuspendLayout
();
this
.
panelSqlInputHeader
.
SuspendLayout
();
this
.
panelSqlResult
.
SuspendLayout
();
this
.
tabJson
.
SuspendLayout
();
this
.
tlpJson
.
SuspendLayout
();
this
.
panelJsonInputHeader
.
SuspendLayout
();
this
.
panelJsonResult
.
SuspendLayout
();
this
.
SuspendLayout
();
//
// tabControl
//
this
.
tabControl
.
Controls
.
Add
(
this
.
tabSql
);
this
.
tabControl
.
Controls
.
Add
(
this
.
tabJson
);
this
.
tabControl
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
tabControl
.
Location
=
new
System
.
Drawing
.
Point
(
0
,
0
);
this
.
tabControl
.
Name
=
"tabControl"
;
this
.
tabControl
.
SelectedIndex
=
0
;
this
.
tabControl
.
Size
=
new
System
.
Drawing
.
Size
(
800
,
580
);
this
.
tabControl
.
TabIndex
=
0
;
//
// tabSql
//
this
.
tabSql
.
Controls
.
Add
(
this
.
tlpSql
);
this
.
tabSql
.
Location
=
new
System
.
Drawing
.
Point
(
4
,
26
);
this
.
tabSql
.
Name
=
"tabSql"
;
this
.
tabSql
.
Padding
=
new
System
.
Windows
.
Forms
.
Padding
(
3
);
this
.
tabSql
.
Size
=
new
System
.
Drawing
.
Size
(
792
,
550
);
this
.
tabSql
.
TabIndex
=
0
;
this
.
tabSql
.
Text
=
"SQL格式化"
;
this
.
tabSql
.
UseVisualStyleBackColor
=
true
;
//
// tlpSql
//
this
.
tlpSql
.
ColumnCount
=
3
;
this
.
tlpSql
.
ColumnStyles
.
Add
(
new
System
.
Windows
.
Forms
.
ColumnStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpSql
.
ColumnStyles
.
Add
(
new
System
.
Windows
.
Forms
.
ColumnStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Percent
,
100F
));
this
.
tlpSql
.
ColumnStyles
.
Add
(
new
System
.
Windows
.
Forms
.
ColumnStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpSql
.
Controls
.
Add
(
this
.
panelSqlInputHeader
,
1
,
1
);
this
.
tlpSql
.
Controls
.
Add
(
this
.
tbSqlInput
,
1
,
2
);
this
.
tlpSql
.
Controls
.
Add
(
this
.
panelSqlResult
,
1
,
3
);
this
.
tlpSql
.
Controls
.
Add
(
this
.
tbSqlOutput
,
1
,
4
);
this
.
tlpSql
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
tlpSql
.
Location
=
new
System
.
Drawing
.
Point
(
3
,
3
);
this
.
tlpSql
.
Name
=
"tlpSql"
;
this
.
tlpSql
.
RowCount
=
6
;
this
.
tlpSql
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpSql
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
30F
));
this
.
tlpSql
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Percent
,
45F
));
this
.
tlpSql
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
30F
));
this
.
tlpSql
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Percent
,
55F
));
this
.
tlpSql
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpSql
.
Size
=
new
System
.
Drawing
.
Size
(
786
,
544
);
this
.
tlpSql
.
TabIndex
=
0
;
//
// panelSqlInputHeader
//
this
.
panelSqlInputHeader
.
Controls
.
Add
(
this
.
lblSqlInput
);
this
.
panelSqlInputHeader
.
Controls
.
Add
(
this
.
lblDialect
);
this
.
panelSqlInputHeader
.
Controls
.
Add
(
this
.
cbSqlDialect
);
this
.
panelSqlInputHeader
.
Controls
.
Add
(
this
.
btnFormatSql
);
this
.
panelSqlInputHeader
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
panelSqlInputHeader
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
9
);
this
.
panelSqlInputHeader
.
Name
=
"panelSqlInputHeader"
;
this
.
panelSqlInputHeader
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
24
);
this
.
panelSqlInputHeader
.
TabIndex
=
0
;
//
// lblSqlInput
//
this
.
lblSqlInput
.
AutoSize
=
true
;
this
.
lblSqlInput
.
Location
=
new
System
.
Drawing
.
Point
(
0
,
4
);
this
.
lblSqlInput
.
Name
=
"lblSqlInput"
;
this
.
lblSqlInput
.
Size
=
new
System
.
Drawing
.
Size
(
82
,
17
);
this
.
lblSqlInput
.
TabIndex
=
0
;
this
.
lblSqlInput
.
Text
=
"输入SQL文本:"
;
this
.
lblSqlInput
.
TextAlign
=
System
.
Drawing
.
ContentAlignment
.
MiddleLeft
;
//
// lblDialect
//
this
.
lblDialect
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
lblDialect
.
AutoSize
=
true
;
this
.
lblDialect
.
Location
=
new
System
.
Drawing
.
Point
(
498
,
4
);
this
.
lblDialect
.
Name
=
"lblDialect"
;
this
.
lblDialect
.
Size
=
new
System
.
Drawing
.
Size
(
58
,
17
);
this
.
lblDialect
.
TabIndex
=
1
;
this
.
lblDialect
.
Text
=
"SQL方言:"
;
this
.
lblDialect
.
TextAlign
=
System
.
Drawing
.
ContentAlignment
.
MiddleLeft
;
//
// cbSqlDialect
//
this
.
cbSqlDialect
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
cbSqlDialect
.
DropDownStyle
=
System
.
Windows
.
Forms
.
ComboBoxStyle
.
DropDownList
;
this
.
cbSqlDialect
.
FormattingEnabled
=
true
;
this
.
cbSqlDialect
.
Location
=
new
System
.
Drawing
.
Point
(
557
,
0
);
this
.
cbSqlDialect
.
Name
=
"cbSqlDialect"
;
this
.
cbSqlDialect
.
Size
=
new
System
.
Drawing
.
Size
(
110
,
25
);
this
.
cbSqlDialect
.
TabIndex
=
2
;
//
// btnFormatSql
//
this
.
btnFormatSql
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnFormatSql
.
Location
=
new
System
.
Drawing
.
Point
(
675
,
0
);
this
.
btnFormatSql
.
Name
=
"btnFormatSql"
;
this
.
btnFormatSql
.
Size
=
new
System
.
Drawing
.
Size
(
90
,
25
);
this
.
btnFormatSql
.
TabIndex
=
3
;
this
.
btnFormatSql
.
Text
=
"SQL格式化"
;
this
.
btnFormatSql
.
UseVisualStyleBackColor
=
true
;
this
.
btnFormatSql
.
Click
+=
new
System
.
EventHandler
(
this
.
btnFormatSql_Click
);
//
// tbSqlInput
//
this
.
tbSqlInput
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
tbSqlInput
.
Font
=
new
System
.
Drawing
.
Font
(
"Consolas"
,
9.75F
,
System
.
Drawing
.
FontStyle
.
Regular
,
System
.
Drawing
.
GraphicsUnit
.
Point
,
((
byte
)(
0
)));
this
.
tbSqlInput
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
39
);
this
.
tbSqlInput
.
Multiline
=
true
;
this
.
tbSqlInput
.
Name
=
"tbSqlInput"
;
this
.
tbSqlInput
.
ScrollBars
=
System
.
Windows
.
Forms
.
ScrollBars
.
Both
;
this
.
tbSqlInput
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
206
);
this
.
tbSqlInput
.
TabIndex
=
2
;
this
.
tbSqlInput
.
WordWrap
=
false
;
//
// panelSqlResult
//
this
.
panelSqlResult
.
Controls
.
Add
(
this
.
lblSqlOutput
);
this
.
panelSqlResult
.
Controls
.
Add
(
this
.
btnCopySql
);
this
.
panelSqlResult
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
panelSqlResult
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
251
);
this
.
panelSqlResult
.
Name
=
"panelSqlResult"
;
this
.
panelSqlResult
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
24
);
this
.
panelSqlResult
.
TabIndex
=
3
;
//
// lblSqlOutput
//
this
.
lblSqlOutput
.
AutoSize
=
true
;
this
.
lblSqlOutput
.
Location
=
new
System
.
Drawing
.
Point
(
0
,
4
);
this
.
lblSqlOutput
.
Name
=
"lblSqlOutput"
;
this
.
lblSqlOutput
.
Size
=
new
System
.
Drawing
.
Size
(
71
,
17
);
this
.
lblSqlOutput
.
TabIndex
=
0
;
this
.
lblSqlOutput
.
Text
=
"格式化结果:"
;
this
.
lblSqlOutput
.
TextAlign
=
System
.
Drawing
.
ContentAlignment
.
MiddleLeft
;
//
// btnCopySql
//
this
.
btnCopySql
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnCopySql
.
Location
=
new
System
.
Drawing
.
Point
(
675
,
0
);
this
.
btnCopySql
.
Name
=
"btnCopySql"
;
this
.
btnCopySql
.
Size
=
new
System
.
Drawing
.
Size
(
90
,
25
);
this
.
btnCopySql
.
TabIndex
=
0
;
this
.
btnCopySql
.
Text
=
"复制结果"
;
this
.
btnCopySql
.
UseVisualStyleBackColor
=
true
;
this
.
btnCopySql
.
Click
+=
new
System
.
EventHandler
(
this
.
btnCopySql_Click
);
//
// tbSqlOutput
//
this
.
tbSqlOutput
.
BackColor
=
System
.
Drawing
.
SystemColors
.
ControlLightLight
;
this
.
tbSqlOutput
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
tbSqlOutput
.
Font
=
new
System
.
Drawing
.
Font
(
"Consolas"
,
9.75F
,
System
.
Drawing
.
FontStyle
.
Regular
,
System
.
Drawing
.
GraphicsUnit
.
Point
,
((
byte
)(
0
)));
this
.
tbSqlOutput
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
281
);
this
.
tbSqlOutput
.
Multiline
=
true
;
this
.
tbSqlOutput
.
Name
=
"tbSqlOutput"
;
this
.
tbSqlOutput
.
ReadOnly
=
true
;
this
.
tbSqlOutput
.
ScrollBars
=
System
.
Windows
.
Forms
.
ScrollBars
.
Both
;
this
.
tbSqlOutput
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
253
);
this
.
tbSqlOutput
.
TabIndex
=
4
;
this
.
tbSqlOutput
.
WordWrap
=
false
;
//
// tabJson
//
this
.
tabJson
.
Controls
.
Add
(
this
.
tlpJson
);
this
.
tabJson
.
Location
=
new
System
.
Drawing
.
Point
(
4
,
26
);
this
.
tabJson
.
Name
=
"tabJson"
;
this
.
tabJson
.
Padding
=
new
System
.
Windows
.
Forms
.
Padding
(
3
);
this
.
tabJson
.
Size
=
new
System
.
Drawing
.
Size
(
792
,
550
);
this
.
tabJson
.
TabIndex
=
1
;
this
.
tabJson
.
Text
=
"JSON格式化"
;
this
.
tabJson
.
UseVisualStyleBackColor
=
true
;
//
// tlpJson
//
this
.
tlpJson
.
ColumnCount
=
3
;
this
.
tlpJson
.
ColumnStyles
.
Add
(
new
System
.
Windows
.
Forms
.
ColumnStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpJson
.
ColumnStyles
.
Add
(
new
System
.
Windows
.
Forms
.
ColumnStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Percent
,
100F
));
this
.
tlpJson
.
ColumnStyles
.
Add
(
new
System
.
Windows
.
Forms
.
ColumnStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpJson
.
Controls
.
Add
(
this
.
panelJsonInputHeader
,
1
,
1
);
this
.
tlpJson
.
Controls
.
Add
(
this
.
tbJsonInput
,
1
,
2
);
this
.
tlpJson
.
Controls
.
Add
(
this
.
panelJsonResult
,
1
,
3
);
this
.
tlpJson
.
Controls
.
Add
(
this
.
tbJsonOutput
,
1
,
4
);
this
.
tlpJson
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
tlpJson
.
Location
=
new
System
.
Drawing
.
Point
(
3
,
3
);
this
.
tlpJson
.
Name
=
"tlpJson"
;
this
.
tlpJson
.
RowCount
=
6
;
this
.
tlpJson
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpJson
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
30F
));
this
.
tlpJson
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Percent
,
45F
));
this
.
tlpJson
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
30F
));
this
.
tlpJson
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Percent
,
55F
));
this
.
tlpJson
.
RowStyles
.
Add
(
new
System
.
Windows
.
Forms
.
RowStyle
(
System
.
Windows
.
Forms
.
SizeType
.
Absolute
,
6F
));
this
.
tlpJson
.
Size
=
new
System
.
Drawing
.
Size
(
786
,
544
);
this
.
tlpJson
.
TabIndex
=
0
;
//
// panelJsonInputHeader
//
this
.
panelJsonInputHeader
.
Controls
.
Add
(
this
.
lblJsonInput
);
this
.
panelJsonInputHeader
.
Controls
.
Add
(
this
.
btnUnescape
);
this
.
panelJsonInputHeader
.
Controls
.
Add
(
this
.
btnEscape
);
this
.
panelJsonInputHeader
.
Controls
.
Add
(
this
.
btnFormatJson
);
this
.
panelJsonInputHeader
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
panelJsonInputHeader
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
9
);
this
.
panelJsonInputHeader
.
Name
=
"panelJsonInputHeader"
;
this
.
panelJsonInputHeader
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
24
);
this
.
panelJsonInputHeader
.
TabIndex
=
0
;
//
// lblJsonInput
//
this
.
lblJsonInput
.
AutoSize
=
true
;
this
.
lblJsonInput
.
Location
=
new
System
.
Drawing
.
Point
(
0
,
4
);
this
.
lblJsonInput
.
Name
=
"lblJsonInput"
;
this
.
lblJsonInput
.
Size
=
new
System
.
Drawing
.
Size
(
91
,
17
);
this
.
lblJsonInput
.
TabIndex
=
0
;
this
.
lblJsonInput
.
Text
=
"输入JSON文本:"
;
this
.
lblJsonInput
.
TextAlign
=
System
.
Drawing
.
ContentAlignment
.
MiddleLeft
;
//
// btnUnescape
//
this
.
btnUnescape
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnUnescape
.
Location
=
new
System
.
Drawing
.
Point
(
487
,
0
);
this
.
btnUnescape
.
Name
=
"btnUnescape"
;
this
.
btnUnescape
.
Size
=
new
System
.
Drawing
.
Size
(
90
,
25
);
this
.
btnUnescape
.
TabIndex
=
1
;
this
.
btnUnescape
.
Text
=
"去除转义"
;
this
.
btnUnescape
.
UseVisualStyleBackColor
=
true
;
this
.
btnUnescape
.
Click
+=
new
System
.
EventHandler
(
this
.
btnUnescape_Click
);
//
// btnEscape
//
this
.
btnEscape
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnEscape
.
Location
=
new
System
.
Drawing
.
Point
(
582
,
0
);
this
.
btnEscape
.
Name
=
"btnEscape"
;
this
.
btnEscape
.
Size
=
new
System
.
Drawing
.
Size
(
90
,
25
);
this
.
btnEscape
.
TabIndex
=
2
;
this
.
btnEscape
.
Text
=
"添加转义"
;
this
.
btnEscape
.
UseVisualStyleBackColor
=
true
;
this
.
btnEscape
.
Click
+=
new
System
.
EventHandler
(
this
.
btnEscape_Click
);
//
// btnFormatJson
//
this
.
btnFormatJson
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnFormatJson
.
Location
=
new
System
.
Drawing
.
Point
(
677
,
0
);
this
.
btnFormatJson
.
Name
=
"btnFormatJson"
;
this
.
btnFormatJson
.
Size
=
new
System
.
Drawing
.
Size
(
90
,
25
);
this
.
btnFormatJson
.
TabIndex
=
3
;
this
.
btnFormatJson
.
Text
=
"JSON格式化"
;
this
.
btnFormatJson
.
UseVisualStyleBackColor
=
true
;
this
.
btnFormatJson
.
Click
+=
new
System
.
EventHandler
(
this
.
btnFormatJson_Click
);
//
// tbJsonInput
//
this
.
tbJsonInput
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
tbJsonInput
.
Font
=
new
System
.
Drawing
.
Font
(
"Consolas"
,
9.75F
,
System
.
Drawing
.
FontStyle
.
Regular
,
System
.
Drawing
.
GraphicsUnit
.
Point
,
((
byte
)(
0
)));
this
.
tbJsonInput
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
39
);
this
.
tbJsonInput
.
Multiline
=
true
;
this
.
tbJsonInput
.
Name
=
"tbJsonInput"
;
this
.
tbJsonInput
.
ScrollBars
=
System
.
Windows
.
Forms
.
ScrollBars
.
Both
;
this
.
tbJsonInput
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
206
);
this
.
tbJsonInput
.
TabIndex
=
1
;
this
.
tbJsonInput
.
WordWrap
=
false
;
//
// panelJsonResult
//
this
.
panelJsonResult
.
Controls
.
Add
(
this
.
lblJsonOutput
);
this
.
panelJsonResult
.
Controls
.
Add
(
this
.
btnExpand
);
this
.
panelJsonResult
.
Controls
.
Add
(
this
.
btnCompress
);
this
.
panelJsonResult
.
Controls
.
Add
(
this
.
btnCopyJson
);
this
.
panelJsonResult
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
panelJsonResult
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
251
);
this
.
panelJsonResult
.
Name
=
"panelJsonResult"
;
this
.
panelJsonResult
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
24
);
this
.
panelJsonResult
.
TabIndex
=
2
;
//
// lblJsonOutput
//
this
.
lblJsonOutput
.
AutoSize
=
true
;
this
.
lblJsonOutput
.
Location
=
new
System
.
Drawing
.
Point
(
0
,
4
);
this
.
lblJsonOutput
.
Name
=
"lblJsonOutput"
;
this
.
lblJsonOutput
.
Size
=
new
System
.
Drawing
.
Size
(
71
,
17
);
this
.
lblJsonOutput
.
TabIndex
=
0
;
this
.
lblJsonOutput
.
Text
=
"格式化结果:"
;
this
.
lblJsonOutput
.
TextAlign
=
System
.
Drawing
.
ContentAlignment
.
MiddleLeft
;
//
// btnExpand
//
this
.
btnExpand
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnExpand
.
Location
=
new
System
.
Drawing
.
Point
(
516
,
0
);
this
.
btnExpand
.
Name
=
"btnExpand"
;
this
.
btnExpand
.
Size
=
new
System
.
Drawing
.
Size
(
75
,
25
);
this
.
btnExpand
.
TabIndex
=
1
;
this
.
btnExpand
.
Text
=
"展开"
;
this
.
btnExpand
.
UseVisualStyleBackColor
=
true
;
this
.
btnExpand
.
Click
+=
new
System
.
EventHandler
(
this
.
btnExpand_Click
);
//
// btnCompress
//
this
.
btnCompress
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnCompress
.
Location
=
new
System
.
Drawing
.
Point
(
597
,
0
);
this
.
btnCompress
.
Name
=
"btnCompress"
;
this
.
btnCompress
.
Size
=
new
System
.
Drawing
.
Size
(
75
,
25
);
this
.
btnCompress
.
TabIndex
=
2
;
this
.
btnCompress
.
Text
=
"压缩"
;
this
.
btnCompress
.
UseVisualStyleBackColor
=
true
;
this
.
btnCompress
.
Click
+=
new
System
.
EventHandler
(
this
.
btnCompress_Click
);
//
// btnCopyJson
//
this
.
btnCopyJson
.
Anchor
=
((
System
.
Windows
.
Forms
.
AnchorStyles
)((
System
.
Windows
.
Forms
.
AnchorStyles
.
Top
|
System
.
Windows
.
Forms
.
AnchorStyles
.
Right
)));
this
.
btnCopyJson
.
Location
=
new
System
.
Drawing
.
Point
(
677
,
0
);
this
.
btnCopyJson
.
Name
=
"btnCopyJson"
;
this
.
btnCopyJson
.
Size
=
new
System
.
Drawing
.
Size
(
90
,
25
);
this
.
btnCopyJson
.
TabIndex
=
3
;
this
.
btnCopyJson
.
Text
=
"复制结果"
;
this
.
btnCopyJson
.
UseVisualStyleBackColor
=
true
;
this
.
btnCopyJson
.
Click
+=
new
System
.
EventHandler
(
this
.
btnCopyJson_Click
);
//
// tbJsonOutput
//
this
.
tbJsonOutput
.
BackColor
=
System
.
Drawing
.
SystemColors
.
ControlLightLight
;
this
.
tbJsonOutput
.
Dock
=
System
.
Windows
.
Forms
.
DockStyle
.
Fill
;
this
.
tbJsonOutput
.
Font
=
new
System
.
Drawing
.
Font
(
"Consolas"
,
9.75F
,
System
.
Drawing
.
FontStyle
.
Regular
,
System
.
Drawing
.
GraphicsUnit
.
Point
,
((
byte
)(
0
)));
this
.
tbJsonOutput
.
Location
=
new
System
.
Drawing
.
Point
(
9
,
281
);
this
.
tbJsonOutput
.
Multiline
=
true
;
this
.
tbJsonOutput
.
Name
=
"tbJsonOutput"
;
this
.
tbJsonOutput
.
ReadOnly
=
true
;
this
.
tbJsonOutput
.
ScrollBars
=
System
.
Windows
.
Forms
.
ScrollBars
.
Both
;
this
.
tbJsonOutput
.
Size
=
new
System
.
Drawing
.
Size
(
768
,
253
);
this
.
tbJsonOutput
.
TabIndex
=
3
;
this
.
tbJsonOutput
.
WordWrap
=
false
;
//
// wTextFormatter
//
this
.
AutoScaleDimensions
=
new
System
.
Drawing
.
SizeF
(
7F
,
17F
);
this
.
AutoScaleMode
=
System
.
Windows
.
Forms
.
AutoScaleMode
.
Font
;
this
.
ClientSize
=
new
System
.
Drawing
.
Size
(
800
,
580
);
this
.
Controls
.
Add
(
this
.
tabControl
);
this
.
MinimumSize
=
new
System
.
Drawing
.
Size
(
640
,
420
);
this
.
Name
=
"wTextFormatter"
;
this
.
Text
=
"文本格式化"
;
this
.
tabControl
.
ResumeLayout
(
false
);
this
.
tabSql
.
ResumeLayout
(
false
);
this
.
tlpSql
.
ResumeLayout
(
false
);
this
.
tlpSql
.
PerformLayout
();
this
.
panelSqlInputHeader
.
ResumeLayout
(
false
);
this
.
panelSqlInputHeader
.
PerformLayout
();
this
.
panelSqlResult
.
ResumeLayout
(
false
);
this
.
panelSqlResult
.
PerformLayout
();
this
.
tabJson
.
ResumeLayout
(
false
);
this
.
tlpJson
.
ResumeLayout
(
false
);
this
.
tlpJson
.
PerformLayout
();
this
.
panelJsonInputHeader
.
ResumeLayout
(
false
);
this
.
panelJsonInputHeader
.
PerformLayout
();
this
.
panelJsonResult
.
ResumeLayout
(
false
);
this
.
panelJsonResult
.
PerformLayout
();
this
.
ResumeLayout
(
false
);
}
#
endregion
private
System
.
Windows
.
Forms
.
TabControl
tabControl
;
private
System
.
Windows
.
Forms
.
TabPage
tabSql
;
private
System
.
Windows
.
Forms
.
TabPage
tabJson
;
// ---- SQL tab 控件 ----
private
System
.
Windows
.
Forms
.
TableLayoutPanel
tlpSql
;
private
System
.
Windows
.
Forms
.
Panel
panelSqlInputHeader
;
private
System
.
Windows
.
Forms
.
Label
lblDialect
;
private
System
.
Windows
.
Forms
.
ComboBox
cbSqlDialect
;
private
System
.
Windows
.
Forms
.
Button
btnFormatSql
;
private
System
.
Windows
.
Forms
.
Label
lblSqlInput
;
private
System
.
Windows
.
Forms
.
TextBox
tbSqlInput
;
private
System
.
Windows
.
Forms
.
Panel
panelSqlResult
;
private
System
.
Windows
.
Forms
.
Label
lblSqlOutput
;
private
System
.
Windows
.
Forms
.
Button
btnCopySql
;
private
System
.
Windows
.
Forms
.
TextBox
tbSqlOutput
;
// ---- JSON tab 控件 ----
private
System
.
Windows
.
Forms
.
TableLayoutPanel
tlpJson
;
private
System
.
Windows
.
Forms
.
Panel
panelJsonInputHeader
;
private
System
.
Windows
.
Forms
.
Button
btnUnescape
;
private
System
.
Windows
.
Forms
.
Button
btnEscape
;
private
System
.
Windows
.
Forms
.
Button
btnFormatJson
;
private
System
.
Windows
.
Forms
.
Label
lblJsonInput
;
private
System
.
Windows
.
Forms
.
TextBox
tbJsonInput
;
private
System
.
Windows
.
Forms
.
Panel
panelJsonResult
;
private
System
.
Windows
.
Forms
.
Label
lblJsonOutput
;
private
System
.
Windows
.
Forms
.
Button
btnExpand
;
private
System
.
Windows
.
Forms
.
Button
btnCompress
;
private
System
.
Windows
.
Forms
.
Button
btnCopyJson
;
private
System
.
Windows
.
Forms
.
TextBox
tbJsonOutput
;
}
}
Tools/UI/wTextFormatter.cs
0 → 100644
View file @
41b668f8
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Windows.Forms
;
//
using
Newtonsoft.Json
;
using
Newtonsoft.Json.Linq
;
using
Tools.MyCommon
;
using
Tools.MyUtils
;
namespace
Tools.UI
{
public
partial
class
wTextFormatter
:
wBasic
{
/// <summary>
/// 当前选择的SQL方言
/// </summary>
private
string
SelectedDialect
=>
this
.
cbSqlDialect
.
SelectedItem
?.
ToString
()
??
"SqlServer"
;
public
wTextFormatter
()
{
InitializeComponent
();
InitDialectCombo
();
}
/// <summary>
/// 初始化SQL方言下拉框
/// </summary>
private
void
InitDialectCombo
()
{
this
.
cbSqlDialect
.
Items
.
AddRange
(
new
object
[]
{
"SqlServer"
,
"MySQL"
,
"PostgreSQL"
,
"Oracle"
,
"SQLite"
});
this
.
cbSqlDialect
.
SelectedIndex
=
0
;
}
// ==================== SQL Tab ====================
/// <summary>
/// 按钮事件:SQL格式化
/// </summary>
private
void
btnFormatSql_Click
(
object
sender
,
EventArgs
e
)
{
string
input
=
this
.
tbSqlInput
.
Text
.
Trim
();
if
(
string
.
IsNullOrWhiteSpace
(
input
))
{
new
wMsgBox
().
Show
(
"请输入SQL文本"
);
return
;
}
try
{
this
.
tbSqlOutput
.
Text
=
SqlFormatHelper
.
Format
(
input
);
this
.
tbSqlOutput
.
Select
(
0
,
0
);
}
catch
(
Exception
ex
)
{
new
wMsgBox
().
Show
(
"SQL格式化失败:"
+
ex
.
Message
);
}
}
/// <summary>
/// 按钮事件:复制SQL结果
/// </summary>
private
void
btnCopySql_Click
(
object
sender
,
EventArgs
e
)
{
CopyToClipboard
(
this
.
tbSqlOutput
);
}
// ==================== JSON Tab ====================
/// <summary>
/// 按钮事件:JSON格式化
/// </summary>
private
void
btnFormatJson_Click
(
object
sender
,
EventArgs
e
)
{
string
input
=
this
.
tbJsonInput
.
Text
.
Trim
();
if
(
string
.
IsNullOrWhiteSpace
(
input
))
{
new
wMsgBox
().
Show
(
"请输入JSON文本"
);
return
;
}
try
{
this
.
tbJsonOutput
.
Text
=
FormatJson
(
input
);
this
.
tbJsonOutput
.
Select
(
0
,
0
);
}
catch
(
Exception
ex
)
{
new
wMsgBox
().
Show
(
"JSON格式化失败:"
+
ex
.
Message
);
}
}
/// <summary>
/// 按钮事件:去除转义 — 将输入中的JSON转义字符还原
/// </summary>
private
void
btnUnescape_Click
(
object
sender
,
EventArgs
e
)
{
string
input
=
this
.
tbJsonInput
.
Text
.
Trim
();
if
(
string
.
IsNullOrWhiteSpace
(
input
))
{
new
wMsgBox
().
Show
(
"请输入JSON文本"
);
return
;
}
try
{
this
.
tbJsonInput
.
Text
=
UnescapeJson
(
input
);
this
.
tbJsonInput
.
Select
(
0
,
0
);
}
catch
(
Exception
ex
)
{
new
wMsgBox
().
Show
(
"去除转义失败:"
+
ex
.
Message
);
}
}
/// <summary>
/// 按钮事件:转义 — 将输入文本转为JSON字符串字面量(对引号/换行等添加\转义)
/// </summary>
private
void
btnEscape_Click
(
object
sender
,
EventArgs
e
)
{
string
input
=
this
.
tbJsonInput
.
Text
.
Trim
();
if
(
string
.
IsNullOrWhiteSpace
(
input
))
{
new
wMsgBox
().
Show
(
"请输入JSON文本"
);
return
;
}
try
{
this
.
tbJsonInput
.
Text
=
EscapeJson
(
input
);
this
.
tbJsonInput
.
Select
(
0
,
0
);
}
catch
(
Exception
ex
)
{
new
wMsgBox
().
Show
(
"转义失败:"
+
ex
.
Message
);
}
}
/// <summary>
/// 按钮事件:压缩 — 将格式化结果压缩为单行紧凑JSON
/// </summary>
private
void
btnCompress_Click
(
object
sender
,
EventArgs
e
)
{
string
output
=
this
.
tbJsonOutput
.
Text
.
Trim
();
if
(
string
.
IsNullOrWhiteSpace
(
output
))
{
new
wMsgBox
().
Show
(
"请先格式化JSON"
);
return
;
}
try
{
this
.
tbJsonOutput
.
Text
=
CompressJson
(
output
);
this
.
tbJsonOutput
.
Select
(
0
,
0
);
}
catch
(
Exception
ex
)
{
new
wMsgBox
().
Show
(
"压缩失败:"
+
ex
.
Message
);
}
}
/// <summary>
/// 按钮事件:展开 — 将压缩的单行JSON重新格式化为缩进多行
/// </summary>
private
void
btnExpand_Click
(
object
sender
,
EventArgs
e
)
{
string
output
=
this
.
tbJsonOutput
.
Text
.
Trim
();
if
(
string
.
IsNullOrWhiteSpace
(
output
))
{
new
wMsgBox
().
Show
(
"请先输入或格式化JSON"
);
return
;
}
try
{
this
.
tbJsonOutput
.
Text
=
FormatJson
(
output
);
this
.
tbJsonOutput
.
Select
(
0
,
0
);
}
catch
(
Exception
ex
)
{
new
wMsgBox
().
Show
(
"展开失败:"
+
ex
.
Message
);
}
}
/// <summary>
/// 按钮事件:复制JSON结果
/// </summary>
private
void
btnCopyJson_Click
(
object
sender
,
EventArgs
e
)
{
CopyToClipboard
(
this
.
tbJsonOutput
);
}
// ==================== 公共方法 ====================
/// <summary>
/// 复制文本框内容到剪贴板
/// </summary>
private
static
void
CopyToClipboard
(
TextBox
textBox
)
{
if
(!
string
.
IsNullOrWhiteSpace
(
textBox
.
Text
))
{
Clipboard
.
SetText
(
textBox
.
Text
);
textBox
.
Focus
();
textBox
.
SelectAll
();
}
}
/// <summary>
/// 格式化JSON文本
/// </summary>
private
static
string
FormatJson
(
string
json
)
{
JToken
token
=
JToken
.
Parse
(
json
);
return
token
.
ToString
(
Formatting
.
Indented
);
}
/// <summary>
/// 去除JSON转义 — 将 \" \n \t \\ 等转义字符还原为原始字符
/// </summary>
private
static
string
UnescapeJson
(
string
json
)
{
// 若输入整体是一个JSON字符串字面量(首尾双引号),则解析取出内部值
if
(
json
.
Length
>=
2
&&
json
[
0
]
==
'"'
&&
json
[
json
.
Length
-
1
]
==
'"'
)
{
try
{
JToken
token
=
JToken
.
Parse
(
json
);
if
(
token
.
Type
==
JTokenType
.
String
)
return
token
.
Value
<
string
>();
}
catch
{
/* 解析失败则回退到逐字符替换 */
}
}
// 回退方案:逐字符还原常见转义
var
sb
=
new
StringBuilder
(
json
.
Length
);
for
(
int
i
=
0
;
i
<
json
.
Length
;
i
++)
{
if
(
json
[
i
]
==
'\\'
&&
i
+
1
<
json
.
Length
)
{
char
next
=
json
[
i
+
1
];
switch
(
next
)
{
case
'"'
:
sb
.
Append
(
'"'
);
i
++;
break
;
case
'\\'
:
sb
.
Append
(
'\\'
);
i
++;
break
;
case
'/'
:
sb
.
Append
(
'/'
);
i
++;
break
;
case
'n'
:
sb
.
Append
(
'\n'
);
i
++;
break
;
case
't'
:
sb
.
Append
(
'\t'
);
i
++;
break
;
case
'r'
:
sb
.
Append
(
'\r'
);
i
++;
break
;
case
'b'
:
sb
.
Append
(
'\b'
);
i
++;
break
;
case
'f'
:
sb
.
Append
(
'\f'
);
i
++;
break
;
case
'u'
:
if
(
i
+
5
<
json
.
Length
)
{
string
hex
=
json
.
Substring
(
i
+
2
,
4
);
if
(
int
.
TryParse
(
hex
,
System
.
Globalization
.
NumberStyles
.
HexNumber
,
null
,
out
int
code
))
{
sb
.
Append
((
char
)
code
);
i
+=
5
;
}
else
{
sb
.
Append
(
json
[
i
]);
}
}
else
{
sb
.
Append
(
json
[
i
]);
}
break
;
default
:
sb
.
Append
(
json
[
i
]);
break
;
}
}
else
{
sb
.
Append
(
json
[
i
]);
}
}
return
sb
.
ToString
();
}
/// <summary>
/// 转义 — 将普通文本转为JSON字符串字面量(加外层引号, 转义内部控制字符)
/// </summary>
private
static
string
EscapeJson
(
string
text
)
{
var
sb
=
new
StringBuilder
(
text
.
Length
+
2
);
sb
.
Append
(
'"'
);
foreach
(
char
c
in
text
)
{
switch
(
c
)
{
case
'"'
:
sb
.
Append
(
"\\\""
);
break
;
case
'\\'
:
sb
.
Append
(
"\\\\"
);
break
;
case
'\n'
:
sb
.
Append
(
"\\n"
);
break
;
case
'\t'
:
sb
.
Append
(
"\\t"
);
break
;
case
'\r'
:
sb
.
Append
(
"\\r"
);
break
;
case
'\b'
:
sb
.
Append
(
"\\b"
);
break
;
case
'\f'
:
sb
.
Append
(
"\\f"
);
break
;
default
:
if
(
c
<
0x20
)
sb
.
AppendFormat
(
"\\u{0:X4}"
,
(
int
)
c
);
else
sb
.
Append
(
c
);
break
;
}
}
sb
.
Append
(
'"'
);
return
sb
.
ToString
();
}
/// <summary>
/// 压缩JSON — 去除所有空白,输出单行紧凑格式
/// </summary>
private
static
string
CompressJson
(
string
json
)
{
JToken
token
=
JToken
.
Parse
(
json
);
return
token
.
ToString
(
Formatting
.
None
);
}
/// <summary>
/// SQL格式化辅助类
/// 格式化规则参考 db_helper:
/// - 复合关键字(LEFT JOIN, GROUP BY等)作为整体换行
/// - 短字段列表(≤3)内联, 长字段列表(≥4)每字段一行
/// - JOIN + ON 同行
/// - WHERE 首个条件与WHERE同行, 后续AND/OR换行缩进
/// - GROUP BY / ORDER BY 紧凑同行
/// - 子查询缩进
/// </summary>
private
static
class
SqlFormatHelper
{
private
const
int
IndentSize
=
4
;
private
const
int
MaxInputLength
=
10485760
;
// 10MB 上限
private
const
int
MaxIndent
=
200
;
// 最大缩进空格数
private
const
int
MaxTokenCount
=
200000
;
// 最大 Token 数(输入≤10MB)
// ---- 复合关键字定义 (组成对, 合并为单个 Token) ----
// key: 前缀关键字, value: 可组合的后续关键字集合
private
static
readonly
Dictionary
<
string
,
HashSet
<
string
>>
CompoundPairs
=
new
Dictionary
<
string
,
HashSet
<
string
>>(
StringComparer
.
OrdinalIgnoreCase
)
{
{
"LEFT"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"JOIN"
,
"OUTER"
}
},
{
"RIGHT"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"JOIN"
,
"OUTER"
}
},
{
"INNER"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"JOIN"
}
},
{
"CROSS"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"JOIN"
}
},
{
"FULL"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"JOIN"
,
"OUTER"
}
},
{
"OUTER"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"JOIN"
}
},
{
"GROUP"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"BY"
}
},
{
"ORDER"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"BY"
}
},
{
"INSERT"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"INTO"
}
},
{
"DELETE"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"FROM"
}
},
{
"UNION"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"ALL"
}
},
{
"FETCH"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"NEXT"
}
},
{
"IF"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"NOT"
}
},
{
"CREATE"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"OR"
}
},
{
"NOT"
,
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"EXISTS"
}
},
};
// 需要独立换行的子句关键字(复合形式优先)
private
static
readonly
HashSet
<
string
>
ClauseStarters
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"SELECT"
,
"FROM"
,
"WHERE"
,
"GROUP BY"
,
"ORDER BY"
,
"HAVING"
,
"INSERT INTO"
,
"UPDATE"
,
"DELETE FROM"
,
"SET"
,
"VALUES"
,
"CREATE"
,
"ALTER"
,
"DROP"
,
"TRUNCATE"
,
"LEFT JOIN"
,
"RIGHT JOIN"
,
"INNER JOIN"
,
"CROSS JOIN"
,
"FULL JOIN"
,
"LEFT OUTER JOIN"
,
"RIGHT OUTER JOIN"
,
"FULL OUTER JOIN"
,
"UNION"
,
"UNION ALL"
,
"INTERSECT"
,
"EXCEPT"
,
"MINUS"
,
"LIMIT"
,
"OFFSET"
,
"FETCH NEXT"
,
"WITH"
,
"RETURNING"
,
"MERGE"
,
"AND"
,
"OR"
,
"WHEN"
,
"ELSE"
,
"BEGIN"
,
"COMMIT"
,
"ROLLBACK"
,
"DECLARE"
,
"GO"
,
};
// 所有需要大写的SQL关键字
private
static
readonly
HashSet
<
string
>
AllKeywords
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
)
{
"SELECT"
,
"FROM"
,
"WHERE"
,
"AND"
,
"OR"
,
"NOT"
,
"IN"
,
"EXISTS"
,
"BETWEEN"
,
"LIKE"
,
"IS"
,
"NULL"
,
"JOIN"
,
"INNER"
,
"LEFT"
,
"RIGHT"
,
"FULL"
,
"CROSS"
,
"OUTER"
,
"ON"
,
"GROUP"
,
"BY"
,
"ORDER"
,
"ASC"
,
"DESC"
,
"HAVING"
,
"INSERT"
,
"INTO"
,
"VALUES"
,
"UPDATE"
,
"SET"
,
"DELETE"
,
"CREATE"
,
"ALTER"
,
"DROP"
,
"TABLE"
,
"INDEX"
,
"VIEW"
,
"PROCEDURE"
,
"FUNCTION"
,
"TRIGGER"
,
"UNION"
,
"ALL"
,
"INTERSECT"
,
"EXCEPT"
,
"MINUS"
,
"LIMIT"
,
"OFFSET"
,
"FETCH"
,
"NEXT"
,
"ROWS"
,
"ONLY"
,
"TOP"
,
"PERCENT"
,
"CASE"
,
"WHEN"
,
"THEN"
,
"ELSE"
,
"END"
,
"AS"
,
"DISTINCT"
,
"COUNT"
,
"SUM"
,
"AVG"
,
"MIN"
,
"MAX"
,
"BEGIN"
,
"COMMIT"
,
"ROLLBACK"
,
"TRANSACTION"
,
"DECLARE"
,
"EXEC"
,
"EXECUTE"
,
"PRIMARY"
,
"KEY"
,
"FOREIGN"
,
"REFERENCES"
,
"CONSTRAINT"
,
"DEFAULT"
,
"CHECK"
,
"UNIQUE"
,
"CASCADE"
,
"WITH"
,
"RECURSIVE"
,
"RETURNING"
,
"MERGE"
,
"USING"
,
"MATCHED"
,
"IF"
,
"WHILE"
,
"FOR"
,
"RETURN"
,
"CAST"
,
"CONVERT"
,
"COALESCE"
,
"NULLIF"
,
"OVER"
,
"PARTITION"
,
"ROW_NUMBER"
,
"RANK"
,
"DENSE_RANK"
,
"SCHEMA"
,
"DATABASE"
,
"USE"
,
"GRANT"
,
"REVOKE"
,
"DENY"
,
"TRUNCATE"
,
"EXPLAIN"
,
"ANALYZE"
,
"REPLACE"
,
"AUTOINCREMENT"
,
"ROWID"
,
"VACUUM"
,
"ILIKE"
,
"LATERAL"
,
"CONNECT"
,
"START"
,
"PRIOR"
,
"NOCYCLE"
,
};
/// <summary>
/// 格式化SQL文本(入口)
/// </summary>
public
static
string
Format
(
string
sql
)
{
if
(
string
.
IsNullOrWhiteSpace
(
sql
))
return
sql
;
// 超长文本保护
if
(
sql
.
Length
>
MaxInputLength
)
throw
new
InvalidOperationException
(
$"输入文本过长(
{
sql
.
Length
}
字符), 最大支持
{
MaxInputLength
}
字符。请分段处理。"
);
var
tokens
=
Tokenize
(
sql
);
if
(
tokens
.
Count
>
MaxTokenCount
)
throw
new
InvalidOperationException
(
$"Token数量(
{
tokens
.
Count
}
)超过上限(
{
MaxTokenCount
}
),请分段处理。"
);
tokens
=
CombineCompounds
(
tokens
);
var
result
=
FormatTokens
(
tokens
);
return
result
.
ToString
();
}
// ==================== Token 类型 ====================
private
enum
TokenType
{
Whitespace
,
Keyword
,
Identifier
,
BracketedIdentifier
,
QuotedIdentifier
,
BacktickIdentifier
,
StringLiteral
,
Number
,
Operator
,
Comma
,
OpenParen
,
CloseParen
,
Semicolon
,
Dot
,
Comment
,
Unknown
,
}
private
struct
Token
{
public
TokenType
Type
;
public
string
Value
;
public
int
Position
;
public
Token
(
TokenType
type
,
string
value
,
int
position
)
{
Type
=
type
;
Value
=
value
;
Position
=
position
;
}
}
// ==================== Phase 1: Tokenize ====================
/// <summary>
/// 将SQL文本拆分为Token列表, 带完整数组边界保护
/// </summary>
private
static
List
<
Token
>
Tokenize
(
string
sql
)
{
var
tokens
=
new
List
<
Token
>();
int
len
=
sql
.
Length
;
if
(
len
==
0
)
return
tokens
;
int
i
=
0
;
while
(
i
<
len
)
{
// Token 数量上限检查(提前中断,避免 List 扩容触发数组越界)
if
(
tokens
.
Count
>=
MaxTokenCount
)
throw
new
InvalidOperationException
(
$"Token数量超过上限(
{
MaxTokenCount
}
),请分段处理。当前已解析到位置
{
i
}
。"
);
// 防御: 确保索引不越界
if
(
i
>=
len
)
break
;
char
c
=
sql
[
i
];
// 空白字符
if
(
char
.
IsWhiteSpace
(
c
))
{
int
start
=
i
;
while
(
i
<
len
&&
char
.
IsWhiteSpace
(
sql
[
i
]))
i
++;
tokens
.
Add
(
new
Token
(
TokenType
.
Whitespace
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 行注释 --
if
(
c
==
'-'
&&
SafeCharAt
(
sql
,
i
+
1
,
len
)
==
'-'
)
{
int
start
=
i
;
while
(
i
<
len
&&
sql
[
i
]
!=
'\n'
&&
sql
[
i
]
!=
'\r'
)
i
++;
tokens
.
Add
(
new
Token
(
TokenType
.
Comment
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 块注释 /* */
if
(
c
==
'/'
&&
SafeCharAt
(
sql
,
i
+
1
,
len
)
==
'*'
)
{
int
start
=
i
;
i
+=
2
;
while
(
i
+
1
<
len
&&
!(
sql
[
i
]
==
'*'
&&
sql
[
i
+
1
]
==
'/'
))
i
++;
if
(
i
+
1
<
len
)
i
+=
2
;
// 跳过结束符 */
tokens
.
Add
(
new
Token
(
TokenType
.
Comment
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 字符串字面量 'text'
if
(
c
==
'\''
)
{
int
start
=
i
;
i
++;
while
(
i
<
len
)
{
if
(
sql
[
i
]
==
'\''
)
{
if
(
SafeCharAt
(
sql
,
i
+
1
,
len
)
==
'\''
)
{
i
+=
2
;
// 转义的单引号
}
else
{
i
++;
break
;
}
}
else
{
i
++;
}
}
tokens
.
Add
(
new
Token
(
TokenType
.
StringLiteral
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 括号标识符 [name]
if
(
c
==
'['
)
{
int
start
=
i
;
i
++;
while
(
i
<
len
&&
sql
[
i
]
!=
']'
)
i
++;
if
(
i
<
len
)
i
++;
// skip ]
tokens
.
Add
(
new
Token
(
TokenType
.
BracketedIdentifier
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 反引号标识符 `name` (MySQL)
if
(
c
==
'`'
)
{
int
start
=
i
;
i
++;
while
(
i
<
len
&&
sql
[
i
]
!=
'`'
)
i
++;
if
(
i
<
len
)
i
++;
tokens
.
Add
(
new
Token
(
TokenType
.
BacktickIdentifier
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 双引号标识符 "name" (PostgreSQL/Oracle)
if
(
c
==
'"'
)
{
int
start
=
i
;
i
++;
while
(
i
<
len
)
{
if
(
sql
[
i
]
==
'"'
)
{
if
(
SafeCharAt
(
sql
,
i
+
1
,
len
)
==
'"'
)
{
i
+=
2
;
}
else
{
i
++;
break
;
}
}
else
{
i
++;
}
}
tokens
.
Add
(
new
Token
(
TokenType
.
QuotedIdentifier
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 数字
if
(
char
.
IsDigit
(
c
))
{
int
start
=
i
;
while
(
i
<
len
&&
(
char
.
IsDigit
(
sql
[
i
])
||
sql
[
i
]
==
'.'
))
i
++;
tokens
.
Add
(
new
Token
(
TokenType
.
Number
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 单字符符号
if
(
c
==
','
)
{
tokens
.
Add
(
new
Token
(
TokenType
.
Comma
,
","
,
i
));
i
++;
continue
;
}
if
(
c
==
';'
)
{
tokens
.
Add
(
new
Token
(
TokenType
.
Semicolon
,
";"
,
i
));
i
++;
continue
;
}
if
(
c
==
'.'
)
{
tokens
.
Add
(
new
Token
(
TokenType
.
Dot
,
"."
,
i
));
i
++;
continue
;
}
if
(
c
==
'('
)
{
tokens
.
Add
(
new
Token
(
TokenType
.
OpenParen
,
"("
,
i
));
i
++;
continue
;
}
if
(
c
==
')'
)
{
tokens
.
Add
(
new
Token
(
TokenType
.
CloseParen
,
")"
,
i
));
i
++;
continue
;
}
// 运算符 (仅匹配已知运算符字符)
if
(
IsOperatorChar
(
c
))
{
int
start
=
i
;
if
(
i
+
1
<
len
&&
IsSecondOpChar
(
sql
[
i
+
1
])
&&
IsCompoundOpFirst
(
c
))
{
i
+=
2
;
}
else
{
i
++;
}
tokens
.
Add
(
new
Token
(
TokenType
.
Operator
,
SubstringSafe
(
sql
,
start
,
i
-
start
),
start
));
continue
;
}
// 常规标识符 (含 @ # : 前缀, 用于变量/参数/命名参数等)
if
(
char
.
IsLetter
(
c
)
||
c
==
'_'
||
c
==
'@'
||
c
==
'#'
||
c
==
':'
)
{
int
start
=
i
;
// 先消费前缀字符 @ # : 避免死循环
if
(
c
==
'@'
||
c
==
'#'
||
c
==
':'
)
i
++;
while
(
i
<
len
&&
(
char
.
IsLetterOrDigit
(
sql
[
i
])
||
sql
[
i
]
==
'_'
||
sql
[
i
]
==
'$'
))
i
++;
string
word
=
SubstringSafe
(
sql
,
start
,
i
-
start
);
if
(
AllKeywords
.
Contains
(
word
))
tokens
.
Add
(
new
Token
(
TokenType
.
Keyword
,
word
,
start
));
else
tokens
.
Add
(
new
Token
(
TokenType
.
Identifier
,
word
,
start
));
continue
;
}
// 其他字符(原样保留)
tokens
.
Add
(
new
Token
(
TokenType
.
Unknown
,
c
.
ToString
(),
i
));
i
++;
}
return
tokens
;
}
// ---- 边界安全辅助方法 ----
/// <summary>安全获取指定位置字符, 越界返回 '\0'</summary>
private
static
char
SafeCharAt
(
string
s
,
int
index
,
int
len
)
{
return
(
index
>=
0
&&
index
<
len
)
?
s
[
index
]
:
'\0'
;
}
/// <summary>安全截取子串, 防止越界</summary>
private
static
string
SubstringSafe
(
string
s
,
int
start
,
int
length
)
{
if
(
start
<
0
)
start
=
0
;
if
(
start
>=
s
.
Length
)
return
string
.
Empty
;
int
maxLen
=
s
.
Length
-
start
;
if
(
length
<
0
)
length
=
0
;
if
(
length
>
maxLen
)
length
=
maxLen
;
return
s
.
Substring
(
start
,
length
);
}
private
static
bool
IsOperatorChar
(
char
c
)
{
return
c
==
'='
||
c
==
'<'
||
c
==
'>'
||
c
==
'!'
||
c
==
'+'
||
c
==
'-'
||
c
==
'*'
||
c
==
'/'
||
c
==
'%'
||
c
==
'&'
||
c
==
'|'
||
c
==
'^'
||
c
==
'~'
;
}
private
static
bool
IsSecondOpChar
(
char
c
)
{
return
c
==
'='
||
c
==
'>'
;
}
private
static
bool
IsCompoundOpFirst
(
char
c
)
{
return
c
==
'<'
||
c
==
'>'
||
c
==
'!'
||
c
==
'+'
||
c
==
'-'
||
c
==
'*'
||
c
==
'/'
;
}
// ==================== Phase 2: 合并复合关键字 ====================
/// <summary>
/// 将相邻的组成关键字合并为复合关键字 Token。
/// 自动跳过中间的空白 Token,确保 "INNER ␣ JOIN" → "INNER JOIN"。
/// 例: LEFT + JOIN → "LEFT JOIN", GROUP + BY → "GROUP BY"
/// 支持三词合并: LEFT + OUTER + JOIN → "LEFT OUTER JOIN"
/// </summary>
private
static
List
<
Token
>
CombineCompounds
(
List
<
Token
>
tokens
)
{
if
(
tokens
.
Count
<
2
)
return
tokens
;
var
result
=
new
List
<
Token
>(
tokens
.
Count
);
int
maxIdx
=
tokens
.
Count
;
for
(
int
i
=
0
;
i
<
maxIdx
;
i
++)
{
if
(
i
>=
maxIdx
)
break
;
Token
curr
=
tokens
[
i
];
// 非关键字直接保留
if
(
curr
.
Type
!=
TokenType
.
Keyword
)
{
result
.
Add
(
curr
);
continue
;
}
// 找到下一个关键字索引(跳过空白)
int
nextKw
=
NextKeywordIdxSkipWs
(
tokens
,
i
+
1
);
if
(
nextKw
<
0
)
{
result
.
Add
(
curr
);
continue
;
}
string
nextVal
=
tokens
[
nextKw
].
Value
;
// 尝试三词合并: LEFT + OUTER + JOIN
if
(
CompoundPairs
.
TryGetValue
(
curr
.
Value
,
out
var
set1
)
&&
set1
.
Contains
(
nextVal
))
{
int
nextNextKw
=
NextKeywordIdxSkipWs
(
tokens
,
nextKw
+
1
);
if
(
nextNextKw
>=
0
&&
IsKeywordMatch
(
tokens
,
nextNextKw
,
"JOIN"
)
&&
CompoundPairs
.
TryGetValue
(
nextVal
,
out
var
innerSet
)
&&
innerSet
.
Contains
(
"JOIN"
))
{
string
combined
=
string
.
Join
(
" "
,
curr
.
Value
.
ToUpperInvariant
(),
nextVal
.
ToUpperInvariant
(),
tokens
[
nextNextKw
].
Value
.
ToUpperInvariant
());
result
.
Add
(
new
Token
(
TokenType
.
Keyword
,
combined
,
curr
.
Position
));
// 将中间被跳过的空白 Token 也补入(保持位置信息)
AppendWsBetween
(
tokens
,
i
+
1
,
nextNextKw
,
result
);
i
=
nextNextKw
;
continue
;
}
}
// 尝试两词合并
if
(
CompoundPairs
.
TryGetValue
(
curr
.
Value
,
out
var
set2
)
&&
set2
.
Contains
(
nextVal
))
{
string
combined
=
curr
.
Value
.
ToUpperInvariant
()
+
" "
+
nextVal
.
ToUpperInvariant
();
result
.
Add
(
new
Token
(
TokenType
.
Keyword
,
combined
,
curr
.
Position
));
AppendWsBetween
(
tokens
,
i
+
1
,
nextKw
,
result
);
i
=
nextKw
;
continue
;
}
result
.
Add
(
curr
);
}
return
result
;
}
/// <summary>
/// 从 startIdx 开始查找下一个关键字 token(跳过空白,遇非空白非关键字则停止)
/// </summary>
private
static
int
NextKeywordIdxSkipWs
(
List
<
Token
>
tokens
,
int
startIdx
)
{
for
(
int
i
=
startIdx
;
i
<
tokens
.
Count
;
i
++)
{
if
(
tokens
[
i
].
Type
==
TokenType
.
Keyword
)
return
i
;
if
(
tokens
[
i
].
Type
!=
TokenType
.
Whitespace
)
return
-
1
;
// 遇到实际内容(非空白),停止
}
return
-
1
;
}
/// <summary>
/// 将范围 (from, to] 内的空白 Token 追加到结果列表
/// </summary>
private
static
void
AppendWsBetween
(
List
<
Token
>
tokens
,
int
from
,
int
to
,
List
<
Token
>
result
)
{
for
(
int
i
=
from
;
i
<=
to
;
i
++)
{
if
(
tokens
[
i
].
Type
==
TokenType
.
Whitespace
)
result
.
Add
(
tokens
[
i
]);
}
}
/// <summary>检查指定位置 Token 是否匹配给定关键字(忽略大小写)</summary>
private
static
bool
IsKeywordMatch
(
List
<
Token
>
tokens
,
int
index
,
string
expected
)
{
if
(
index
<
0
||
index
>=
tokens
.
Count
)
return
false
;
var
t
=
tokens
[
index
];
return
t
.
Type
==
TokenType
.
Keyword
&&
string
.
Equals
(
t
.
Value
,
expected
,
StringComparison
.
OrdinalIgnoreCase
);
}
// ==================== Phase 3: 格式化 ====================
/// <summary>当前 SQL 子句上下文</summary>
private
enum
ClauseCtx
{
None
,
SelectList
,
// SELECT 字段列表
FromClause
,
// FROM 子句
JoinClause
,
// JOIN 之后, ON 之前
OnCondition
,
// ON 条件
WhereClause
,
// WHERE 条件
GroupBy
,
// GROUP BY
OrderBy
,
// ORDER BY
SetClause
,
// SET
ValuesClause
,
// VALUES
CaseWhen
,
// CASE WHEN
}
/// <summary>
/// 将 Token 列表格式化为 db_helper 风格 SQL
/// </summary>
private
static
StringBuilder
FormatTokens
(
List
<
Token
>
tokens
)
{
var
sb
=
new
StringBuilder
();
int
indent
=
0
;
bool
newline
=
true
;
TokenType
prevType
=
TokenType
.
Whitespace
;
string
prevValue
=
""
;
ClauseCtx
ctx
=
ClauseCtx
.
None
;
int
maxIdx
=
tokens
.
Count
;
for
(
int
ti
=
0
;
ti
<
maxIdx
;
ti
++)
{
if
(
ti
>=
maxIdx
)
break
;
Token
token
=
tokens
[
ti
];
switch
(
token
.
Type
)
{
case
TokenType
.
Whitespace
:
continue
;
case
TokenType
.
Keyword
:
{
string
kw
=
token
.
Value
.
ToUpperInvariant
();
// 点号后的关键字视为普通标识符,不触发子句逻辑
if
(
prevValue
.
Equals
(
"."
,
StringComparison
.
Ordinal
))
{
EmitInline
(
sb
,
ref
newline
,
indent
,
token
.
Value
);
prevType
=
TokenType
.
Identifier
;
prevValue
=
token
.
Value
;
continue
;
}
// ---- AND / OR: WHERE 中换行缩进, ON 中同行 ----
if
(
kw
==
"AND"
||
kw
==
"OR"
)
{
if
(
ctx
==
ClauseCtx
.
WhereClause
)
{
// db_helper: WHERE 首个条件同行, AND/OR 换行缩进
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
indent
+
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
if
(
ctx
==
ClauseCtx
.
OnCondition
)
{
// db_helper: JOIN ON 中的短 AND/OR 同行
if
(
sb
.
Length
>
0
&&
sb
[
sb
.
Length
-
1
]
!=
' '
)
sb
.
Append
(
' '
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// 其他位置: 换行缩进
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
indent
+
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- ON: 跟在 JOIN 同行 ----
if
(
kw
==
"ON"
)
{
// db_helper: JOIN + ON 同行
sb
.
Append
(
' '
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
ctx
=
ClauseCtx
.
OnCondition
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- WHEN / ELSE / THEN / END: CASE 子句 ----
if
(
kw
==
"WHEN"
||
kw
==
"ELSE"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
indent
+
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
ctx
=
ClauseCtx
.
CaseWhen
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
if
(
kw
==
"THEN"
)
{
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
if
(
kw
==
"END"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
indent
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- JOIN 系列关键字(已经合并为复合关键字) ----
if
(
IsJoinKeyword
(
kw
))
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
ctx
=
ClauseCtx
.
JoinClause
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- SET / VALUES ----
if
(
kw
==
"SET"
||
kw
==
"VALUES"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
ctx
=
kw
==
"SET"
?
ClauseCtx
.
SetClause
:
ClauseCtx
.
ValuesClause
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- GROUP BY / ORDER BY / HAVING: 同行紧凑 ----
if
(
kw
==
"GROUP BY"
||
kw
==
"ORDER BY"
||
kw
==
"HAVING"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
ctx
=
kw
==
"GROUP BY"
?
ClauseCtx
.
GroupBy
:
ClauseCtx
.
OrderBy
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- WHERE ----
if
(
kw
==
"WHERE"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
ctx
=
ClauseCtx
.
WhereClause
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- FROM ----
if
(
kw
==
"FROM"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
ctx
=
ClauseCtx
.
FromClause
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- SELECT / INSERT / UPDATE / DELETE / CREATE / ALTER / DROP / WITH / MERGE / TRUNCATE / DECLARE / BEGIN / GO ----
if
(
IsTopLevelKeyword
(
kw
))
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
0
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
indent
=
IndentSize
;
ctx
=
kw
==
"SELECT"
?
ClauseCtx
.
SelectList
:
ClauseCtx
.
None
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- UNION / INTERSECT / EXCEPT / MINUS: 集合操作, 前后空行 ----
if
(
kw
==
"UNION"
||
kw
==
"UNION ALL"
||
kw
==
"INTERSECT"
||
kw
==
"EXCEPT"
||
kw
==
"MINUS"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
sb
.
AppendLine
();
newline
=
true
;
WriteIndent
(
sb
,
ref
newline
,
0
);
sb
.
Append
(
kw
);
sb
.
AppendLine
();
newline
=
true
;
indent
=
IndentSize
;
ctx
=
ClauseCtx
.
None
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- LIMIT / OFFSET / FETCH NEXT ----
if
(
kw
==
"LIMIT"
||
kw
==
"OFFSET"
||
kw
==
"FETCH NEXT"
)
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
IndentSize
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- 其他 ClauseStarters 中的关键字 ----
if
(
ClauseStarters
.
Contains
(
kw
))
{
if
(!
newline
)
{
sb
.
AppendLine
();
newline
=
true
;
}
WriteIndent
(
sb
,
ref
newline
,
indent
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
continue
;
}
// ---- 非子句关键字(AS / IN / NOT / NULL / BY 等): 内联 ----
if
(
NeedsLeadingSpace
(
prevType
))
sb
.
Append
(
' '
);
sb
.
Append
(
kw
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Keyword
;
prevValue
=
kw
;
break
;
}
case
TokenType
.
Comma
:
{
sb
.
Append
(
','
);
// 逗号后换行缩进
sb
.
AppendLine
();
newline
=
true
;
prevType
=
TokenType
.
Comma
;
prevValue
=
","
;
break
;
}
case
TokenType
.
OpenParen
:
sb
.
Append
(
'('
);
indent
=
Math
.
Min
(
indent
+
IndentSize
,
MaxIndent
);
newline
=
false
;
prevType
=
TokenType
.
OpenParen
;
prevValue
=
"("
;
break
;
case
TokenType
.
CloseParen
:
indent
=
Math
.
Max
(
0
,
indent
-
IndentSize
);
if
(
newline
)
{
WriteIndent
(
sb
,
ref
newline
,
indent
);
}
sb
.
Append
(
')'
);
newline
=
false
;
prevType
=
TokenType
.
CloseParen
;
prevValue
=
")"
;
break
;
case
TokenType
.
Semicolon
:
sb
.
Append
(
';'
);
sb
.
AppendLine
();
newline
=
true
;
indent
=
0
;
ctx
=
ClauseCtx
.
None
;
prevType
=
TokenType
.
Semicolon
;
prevValue
=
";"
;
break
;
case
TokenType
.
Dot
:
sb
.
Append
(
'.'
);
newline
=
false
;
prevType
=
TokenType
.
Dot
;
prevValue
=
"."
;
break
;
case
TokenType
.
StringLiteral
:
case
TokenType
.
BracketedIdentifier
:
case
TokenType
.
QuotedIdentifier
:
case
TokenType
.
BacktickIdentifier
:
case
TokenType
.
Number
:
case
TokenType
.
Identifier
:
if
(
newline
)
{
WriteIndent
(
sb
,
ref
newline
,
indent
);
}
// 仅由当前 token 根据前驱类型决定是否补前导空格
// Keyword/Operator 已在自身末尾追加空格,故排除,避免双空格
if
(
NeedsLeadingSpace
(
prevType
))
sb
.
Append
(
' '
);
sb
.
Append
(
token
.
Value
);
newline
=
false
;
prevType
=
token
.
Type
;
prevValue
=
token
.
Value
;
break
;
case
TokenType
.
Operator
:
if
(
newline
)
{
WriteIndent
(
sb
,
ref
newline
,
indent
);
}
sb
.
Append
(
' '
);
sb
.
Append
(
token
.
Value
);
sb
.
Append
(
' '
);
newline
=
false
;
prevType
=
TokenType
.
Operator
;
prevValue
=
token
.
Value
;
break
;
case
TokenType
.
Comment
:
if
(!
newline
)
sb
.
Append
(
' '
);
sb
.
Append
(
token
.
Value
);
newline
=
false
;
prevType
=
TokenType
.
Comment
;
prevValue
=
token
.
Value
;
break
;
default
:
if
(
newline
)
{
WriteIndent
(
sb
,
ref
newline
,
indent
);
}
sb
.
Append
(
token
.
Value
);
newline
=
false
;
prevType
=
token
.
Type
;
prevValue
=
token
.
Value
;
break
;
}
}
return
new
StringBuilder
(
sb
.
ToString
().
Trim
());
}
// ---- 格式化辅助 ----
private
static
void
WriteIndent
(
StringBuilder
sb
,
ref
bool
newline
,
int
spaces
)
{
if
(!
newline
)
return
;
int
safeSpaces
=
spaces
;
if
(
safeSpaces
<
0
)
safeSpaces
=
0
;
if
(
safeSpaces
>
MaxIndent
)
safeSpaces
=
MaxIndent
;
sb
.
Append
(
new
string
(
' '
,
safeSpaces
));
newline
=
false
;
}
private
static
void
EmitInline
(
StringBuilder
sb
,
ref
bool
newline
,
int
indent
,
string
value
)
{
if
(
newline
)
WriteIndent
(
sb
,
ref
newline
,
indent
);
sb
.
Append
(
value
);
newline
=
false
;
}
private
static
bool
IsJoinKeyword
(
string
kw
)
{
return
kw
.
EndsWith
(
" JOIN"
,
StringComparison
.
OrdinalIgnoreCase
)
||
kw
.
Equals
(
"JOIN"
,
StringComparison
.
OrdinalIgnoreCase
);
}
/// <summary>
/// 判断前一个 token 类型是否需要与当前词之间补空格。
/// 排除 Keyword/Operator/Semicolon/Dot — 它们已在自身末尾追加了空格。
/// OpenParen 不需要后置空格 (如 "CAST(" 之后不应有空格)。
/// </summary>
private
static
bool
NeedsLeadingSpace
(
TokenType
prevType
)
{
return
prevType
==
TokenType
.
Identifier
||
prevType
==
TokenType
.
Number
||
prevType
==
TokenType
.
StringLiteral
||
prevType
==
TokenType
.
BracketedIdentifier
||
prevType
==
TokenType
.
QuotedIdentifier
||
prevType
==
TokenType
.
BacktickIdentifier
||
prevType
==
TokenType
.
CloseParen
||
prevType
==
TokenType
.
Comment
;
}
private
static
bool
IsTopLevelKeyword
(
string
kw
)
{
return
kw
==
"SELECT"
||
kw
==
"INSERT"
||
kw
==
"INSERT INTO"
||
kw
==
"UPDATE"
||
kw
==
"DELETE"
||
kw
==
"DELETE FROM"
||
kw
==
"CREATE"
||
kw
==
"ALTER"
||
kw
==
"DROP"
||
kw
==
"WITH"
||
kw
==
"MERGE"
||
kw
==
"TRUNCATE"
||
kw
==
"DECLARE"
||
kw
==
"BEGIN"
||
kw
==
"GO"
||
kw
==
"RETURN"
||
kw
==
"IF"
;
}
}
}
}
Tools/UI/wTextFormatter.resx
0 → 100644
View file @
41b668f8
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema
id=
"root"
xmlns=
""
xmlns:xsd=
"http://www.w3.org/2001/XMLSchema"
xmlns:msdata=
"urn:schemas-microsoft-com:xml-msdata"
>
<xsd:import
namespace=
"http://www.w3.org/XML/1998/namespace"
/>
<xsd:element
name=
"root"
msdata:IsDataSet=
"true"
>
<xsd:complexType>
<xsd:choice
maxOccurs=
"unbounded"
>
<xsd:element
name=
"metadata"
>
<xsd:complexType>
<xsd:sequence>
<xsd:element
name=
"value"
type=
"xsd:string"
minOccurs=
"0"
/>
</xsd:sequence>
<xsd:attribute
name=
"name"
use=
"required"
type=
"xsd:string"
/>
<xsd:attribute
name=
"type"
type=
"xsd:string"
/>
<xsd:attribute
name=
"mimetype"
type=
"xsd:string"
/>
<xsd:attribute
ref=
"xml:space"
/>
</xsd:complexType>
</xsd:element>
<xsd:element
name=
"assembly"
>
<xsd:complexType>
<xsd:attribute
name=
"alias"
type=
"xsd:string"
/>
<xsd:attribute
name=
"name"
type=
"xsd:string"
/>
</xsd:complexType>
</xsd:element>
<xsd:element
name=
"data"
>
<xsd:complexType>
<xsd:sequence>
<xsd:element
name=
"value"
type=
"xsd:string"
minOccurs=
"0"
msdata:Ordinal=
"1"
/>
<xsd:element
name=
"comment"
type=
"xsd:string"
minOccurs=
"0"
msdata:Ordinal=
"2"
/>
</xsd:sequence>
<xsd:attribute
name=
"name"
type=
"xsd:string"
use=
"required"
msdata:Ordinal=
"1"
/>
<xsd:attribute
name=
"type"
type=
"xsd:string"
msdata:Ordinal=
"3"
/>
<xsd:attribute
name=
"mimetype"
type=
"xsd:string"
msdata:Ordinal=
"4"
/>
<xsd:attribute
ref=
"xml:space"
/>
</xsd:complexType>
</xsd:element>
<xsd:element
name=
"resheader"
>
<xsd:complexType>
<xsd:sequence>
<xsd:element
name=
"value"
type=
"xsd:string"
minOccurs=
"0"
msdata:Ordinal=
"1"
/>
</xsd:sequence>
<xsd:attribute
name=
"name"
type=
"xsd:string"
use=
"required"
/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader
name=
"resmimetype"
>
<value>
text/microsoft-resx
</value>
</resheader>
<resheader
name=
"version"
>
<value>
2.0
</value>
</resheader>
<resheader
name=
"reader"
>
<value>
System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader
name=
"writer"
>
<value>
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
</value>
</resheader>
</root>
\ No newline at end of file
Tools/wExtend.cs
View file @
41b668f8
...
@@ -45,6 +45,7 @@ namespace Tools
...
@@ -45,6 +45,7 @@ namespace Tools
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wTaskBoard
),
"任务看板"
,
"tr:任务看板的简易化工具,免除翻页困扰"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wTaskBoard
),
"任务看板"
,
"tr:任务看板的简易化工具,免除翻页困扰"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wLoginPwd
),
"密码解析"
,
"tr:用于登陆密码的解密加密"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wLoginPwd
),
"密码解析"
,
"tr:用于登陆密码的解密加密"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
WindowsSearch
.
FrmSearch
),
"文件搜索"
,
"lxj:用于搜索文件内容"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
WindowsSearch
.
FrmSearch
),
"文件搜索"
,
"lxj:用于搜索文件内容"
),
this
.
CreateModule
(
Tools_uni
,
typeof
(
UI
.
wTextFormatter
),
"文本格式化"
,
"tr:SQL和JSON文本格式化工具"
),
//-------------------------------
//-------------------------------
this
.
CreateModule
(
Tools_imp
,
typeof
(
UpdateExecuter
.
frmMain
),
"更新执行工具"
,
"lxj:用于客户更新和执行SQL脚本"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UpdateExecuter
.
frmMain
),
"更新执行工具"
,
"lxj:用于客户更新和执行SQL脚本"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wEmptyTable
),
"清空表"
,
"tr:输入主表、自动清空关联表的工具"
),
this
.
CreateModule
(
Tools_imp
,
typeof
(
UI
.
wEmptyTable
),
"清空表"
,
"tr:输入主表、自动清空关联表的工具"
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment