-
Notifications
You must be signed in to change notification settings - Fork 59
/
ue4_smarter_macro_indenting_vs2013-2015.vcmd
189 lines (178 loc) · 8.31 KB
/
ue4_smarter_macro_indenting_vs2013-2015.vcmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<?xml version="1.0" encoding="utf-8"?>
<SerializableSnippets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<commands />
<extensions>
<Snippet>
<id>1</id>
<name>UE4 Smarter Macro Indenting</name>
<code>
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio;
using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;
public class E : VisualCommanderExt.IExtension
{
private EnvDTE80.DTE2 _DTE;
private EnvDTE80.TextDocumentKeyPressEvents _textDocumentKeyPressEvents;
private static readonly string MACRO_REGEX = @"^(?<leading_whitespace>[\s]*)(UPROPERTY|UFUNCTION|GENERATED_(USTRUCT_|UCLASS_|(U|I)INTERFACE_)?BODY)\(.*";
private static readonly string TEXT_WITH_WS_REGEX = @"(?<leading_whitespace>[\s]*)\S+";
private CommandEvents _pasteEvent;
private string _beforeText;
private int _numSelectedLines;
public void SetSite(DTE2 DTE, Microsoft.VisualStudio.Shell.Package package) {
_DTE = DTE;
EnvDTE80.Events2 events2 = (EnvDTE80.Events2)DTE.Events;
_textDocumentKeyPressEvents = events2.get_TextDocumentKeyPressEvents(null);
_textDocumentKeyPressEvents.AfterKeyPress += AfterKeyPress;
var pasteGuid = typeof(VSConstants.VSStd97CmdID).GUID.ToString("B");
var pasteID = (int)VSConstants.VSStd97CmdID.Paste;
_pasteEvent = _DTE.Events.CommandEvents[pasteGuid, pasteID];
_pasteEvent.BeforeExecute += BeforePaste;
_pasteEvent.AfterExecute += AfterPaste;
}
public void Close() {
_textDocumentKeyPressEvents.AfterKeyPress -= AfterKeyPress;
_pasteEvent.AfterExecute -= AfterPaste;
_pasteEvent.BeforeExecute -= BeforePaste;
}
private string GetActiveDocumentText() {
TextDocument doc = (TextDocument)(_DTE.ActiveDocument.Object("TextDocument"));
var editPoint = doc.StartPoint.CreateEditPoint();
return editPoint.GetText(doc.EndPoint);
}
private void BeforePaste(string Guid, int ID, Object CustomIn, Object CustomOut, ref bool CancelDefault) {
_beforeText = GetActiveDocumentText();
TextSelection sel = (TextSelection)_DTE.ActiveDocument.Selection;
_numSelectedLines = sel.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None).Length;
}
private void AfterPaste(string Guid, int ID, Object CustomIn, Object CustomOut) {
string afterText = GetActiveDocumentText();
string[] beforeLines = _beforeText.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
string[] afterLines = afterText.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
int pasteLength = (afterLines.Length - beforeLines.Length) + _numSelectedLines;
TextSelection sel = (TextSelection)_DTE.ActiveDocument.Selection;
var editPoint = sel.ActivePoint.CreateEditPoint();
bool underMacro = false;
string lastWhiteSpace = "";
bool openedUndoContext = false;
int startPasteLine = sel.ActivePoint.Line - pasteLength;
int macroLineNum = startPasteLine + 1;
// Search up the document from the start of our paste until we find a line with text on it
// so we can then determine if this line contains a macro we care about
while (--macroLineNum >= 1) {
string macroLine = editPoint.GetLines(macroLineNum, macroLineNum + 1);
if (!Regex.IsMatch(macroLine, @"\S+")) {
continue;
}
var macroMatch = Regex.Match(macroLine, MACRO_REGEX);
if (macroMatch.Success) {
underMacro = true;
lastWhiteSpace = macroMatch.Groups["leading_whitespace"].ToString();
}
break;
}
if (!_DTE.UndoContext.IsOpen) {
openedUndoContext = true;
// Open the UndoContext so all of our changes can be undone with a single undo
_DTE.UndoContext.Open("FixUE4MacroIndents", false);
}
try {
for (int onLine = startPasteLine; onLine < afterLines.Length; onLine++) {
var line = afterLines[onLine];
// We only need to potentially fix the newly pasted lines
if (onLine >= startPasteLine + pasteLength) {
sel.GotoLine(onLine, false);
sel.MoveToPoint(editPoint);
break;
}
if (underMacro) {
var varMatch = Regex.Match(line, TEXT_WITH_WS_REGEX);
if (varMatch.Success && varMatch.Groups["leading_whitespace"].ToString() != lastWhiteSpace) {
sel.GotoLine(onLine + 1, false);
sel.DeleteWhitespace(EnvDTE.vsWhitespaceOptions.vsWhitespaceOptionsHorizontal);
sel.Insert(lastWhiteSpace);
underMacro = false;
// This line has been modified, so change it for the MACRO_REGEX matching below
line = lastWhiteSpace + line.Trim();
}
}
// Its important to check if the current line is a macro
// even if we just fixed the spacing of the current line
var macroMatch = Regex.Match(line, MACRO_REGEX);
if (macroMatch.Success) {
underMacro = true;
lastWhiteSpace = macroMatch.Groups["leading_whitespace"].ToString();
} else if (Regex.Match(line, @"\S+").Success) {
underMacro = false;
}
}
} finally {
if (openedUndoContext) {
_DTE.UndoContext.Close();
}
}
}
private void AfterKeyPress(string key, TextSelection sel, bool completion) {
// Only semicolons or carriage returns should cause an indentation that need to be fixed
if (key != ";" && key != "\r") {
return;
}
// Make sure we're using smart indent
EnvDTE.Properties textEditorC = _DTE.get_Properties("TextEditor", "C/C++");
if ((int)textEditorC.Item("IndentStyle").Value != 2) {
return;
}
if (key == ";") {
// Make sure we're auto-formatting on semicolons
EnvDTE.Properties textEditorCSpecific = _DTE.get_Properties("TextEditor", "C/C++ Specific");
if (!(bool)textEditorCSpecific.Item("AutoFormatOnSemicolon").Value) {
return;
}
}
var doc = _DTE.ActiveDocument;
var editPoint = sel.ActivePoint.CreateEditPoint();
string macroLine = null;
var macroLineNum = sel.ActivePoint.Line;
// Search up the document from our current line until we find a line with text on it
// so we can then determine if this line contains a macro we care about
var found = false;
while (--macroLineNum >= 1) {
macroLine = editPoint.GetLines(macroLineNum, macroLineNum + 1);
if (!Regex.IsMatch(macroLine, @"\S+")) {
continue;
}
found = true;
break;
}
if (!found) {
return;
}
var macroMatch = Regex.Match(macroLine, MACRO_REGEX);
if (macroMatch.Success) {
// Goto and select our current line, can't do this in a single GotoLine call for some reason
sel.GotoLine(sel.ActivePoint.Line, false);
sel.SelectLine();
if (Regex.IsMatch(sel.Text, @"\S+")) {
// If the line below the macro has text, undo the indent it just did
doc.Undo();
} else {
// If the line below the macro is empty, add matching whitespace to the beginning of this line to match it up with the macro
sel.MoveToPoint(editPoint);
sel.DeleteWhitespace(EnvDTE.vsWhitespaceOptions.vsWhitespaceOptionsHorizontal);
sel.Insert(macroMatch.Groups["leading_whitespace"].ToString());
}
}
}
}</code>
<referencedAssemblies />
<type>Extension</type>
<lang>CS</lang>
<langVersion>v4.0</langVersion>
<enabled>false</enabled>
<includeDebugInformation>false</includeDebugInformation>
</Snippet>
</extensions>
<commonCode />
</SerializableSnippets>