├── LICENSE ├── README.md ├── resources ├── ue4_smarter_macro_indenting_off.gif └── ue4_smarter_macro_indenting_on.gif └── ue4_smarter_macro_indenting.vcmd /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 hackalyze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### UE4 자동 매크로 들여쓰기 2 | 3 | 이 확장팩은 UE4 에서 사용하는 다양한 매크로들에 대해서 짜증나는 자동 들여쓰기 들여쓰기를 고치기 위해 고안 되었습니다. 4 | 다음과 같은 UE4 매크로들을 사용할 때 자동 들여쓰기 동작이 고쳐지도록 시도하였습니다. 5 | 6 | * UPROPERTY 7 | * UFUNCTION 8 | * GENERATED_BODY 9 | * GENERATED_UCLASS_BODY 10 | * GENERATED_USTRUCT_BODY 11 | * GENERATED_UINTERFACE_BODY 12 | * GENERATED_IINTERFACE_BODY 13 | 14 | ### Demo 15 | | Before | After | 16 | | --- | --- | 17 | | ![Example](resources/ue4_smarter_macro_indenting_off.gif "Normal Smart Indention in Visual Studio") | ![Example](resources/ue4_smarter_macro_indenting_on.gif "Extension enabled") | 18 | 19 | ### Installation 20 | 21 | 1. Visual Commander extenstion 다운로드 및 설치 https://vlasovstudio.com/visual-commander/ 22 | 2. VCmd 를 열고 ue4_smarter_macro_indenting.vcmd 파일을 import 23 | 3. VCmd-> Extensions 패널 24 | 4. "UE4 Fix Indent" 체크박스에 "Enabled" 체크 25 | 5. 도구 - 옵션 - 텍스트 편집기 - C/C++ 탭 확인 26 | 27 | > 만약 VCmd가 비쥬얼 스튜디오에 표지되지 않으면, 비쥬얼 스튜디오 재시작. -------------------------------------------------------------------------------- /resources/ue4_smarter_macro_indenting_off.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenoengine/ue4-vs-extensions/1b1cafffb3d9822b6bc27d7c97c202f297a63adb/resources/ue4_smarter_macro_indenting_off.gif -------------------------------------------------------------------------------- /resources/ue4_smarter_macro_indenting_on.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenoengine/ue4-vs-extensions/1b1cafffb3d9822b6bc27d7c97c202f297a63adb/resources/ue4_smarter_macro_indenting_on.gif -------------------------------------------------------------------------------- /ue4_smarter_macro_indenting.vcmd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1 7 | UE4 Smarter Macro Indenting 8 | 9 | using EnvDTE; 10 | using EnvDTE80; 11 | using Microsoft.VisualStudio; 12 | using System; 13 | using System.Text.RegularExpressions; 14 | using System.Windows.Forms; 15 | 16 | public class E : VisualCommanderExt.IExtension 17 | { 18 | private EnvDTE80.DTE2 _DTE; 19 | private EnvDTE80.TextDocumentKeyPressEvents _textDocumentKeyPressEvents; 20 | private static readonly string MACRO_REGEX = @"(?<leading_whitespace>[\s]*)(UPROPERTY|UFUNCTION|GENERATED_(USTRUCT_|UCLASS_|(U|I)INTERFACE_)?BODY)\(.*"; 21 | private static readonly string TEXT_WITH_WS_REGEX = @"(?<leading_whitespace>[\s]*)\S+"; 22 | 23 | private CommandEvents _pasteEvent; 24 | private string _beforeText; 25 | private int _numSelectedLines; 26 | 27 | public void SetSite(DTE2 DTE, Microsoft.VisualStudio.Shell.Package package) { 28 | _DTE = DTE; 29 | EnvDTE80.Events2 events2 = (EnvDTE80.Events2)DTE.Events; 30 | _textDocumentKeyPressEvents = events2.get_TextDocumentKeyPressEvents(null); 31 | _textDocumentKeyPressEvents.AfterKeyPress += AfterKeyPress; 32 | 33 | var pasteGuid = typeof(VSConstants.VSStd97CmdID).GUID.ToString("B"); 34 | var pasteID = (int)VSConstants.VSStd97CmdID.Paste; 35 | _pasteEvent = _DTE.Events.CommandEvents[pasteGuid, pasteID]; 36 | _pasteEvent.BeforeExecute += BeforePaste; 37 | _pasteEvent.AfterExecute += AfterPaste; 38 | } 39 | 40 | public void Close() { 41 | _textDocumentKeyPressEvents.AfterKeyPress -= AfterKeyPress; 42 | _pasteEvent.AfterExecute -= AfterPaste; 43 | _pasteEvent.BeforeExecute -= BeforePaste; 44 | } 45 | 46 | private string GetActiveDocumentText() { 47 | TextDocument doc = (TextDocument)(_DTE.ActiveDocument.Object("TextDocument")); 48 | var editPoint = doc.StartPoint.CreateEditPoint(); 49 | return editPoint.GetText(doc.EndPoint); 50 | } 51 | 52 | private void BeforePaste(string Guid, int ID, Object CustomIn, Object CustomOut, ref bool CancelDefault) { 53 | _beforeText = GetActiveDocumentText(); 54 | TextSelection sel = (TextSelection)_DTE.ActiveDocument.Selection; 55 | _numSelectedLines = sel.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None).Length; 56 | } 57 | 58 | private void AfterPaste(string Guid, int ID, Object CustomIn, Object CustomOut) { 59 | string afterText = GetActiveDocumentText(); 60 | string[] beforeLines = _beforeText.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); 61 | string[] afterLines = afterText.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); 62 | int pasteLength = (afterLines.Length - beforeLines.Length) + _numSelectedLines; 63 | TextSelection sel = (TextSelection)_DTE.ActiveDocument.Selection; 64 | var editPoint = sel.ActivePoint.CreateEditPoint(); 65 | bool underMacro = false; 66 | string lastWhiteSpace = ""; 67 | bool openedUndoContext = false; 68 | int startPasteLine = sel.ActivePoint.Line - pasteLength; 69 | int macroLineNum = startPasteLine + 1; 70 | // Search up the document from the start of our paste until we find a line with text on it 71 | // so we can then determine if this line contains a macro we care about 72 | while (--macroLineNum >= 1) { 73 | string macroLine = editPoint.GetLines(macroLineNum, macroLineNum + 1); 74 | if (!Regex.IsMatch(macroLine, @"\S+")) { 75 | continue; 76 | } 77 | var macroMatch = Regex.Match(macroLine, MACRO_REGEX); 78 | if (macroMatch.Success) { 79 | underMacro = true; 80 | lastWhiteSpace = macroMatch.Groups["leading_whitespace"].ToString(); 81 | } 82 | break; 83 | } 84 | 85 | if (!_DTE.UndoContext.IsOpen) { 86 | openedUndoContext = true; 87 | // Open the UndoContext so all of our changes can be undone with a single undo 88 | _DTE.UndoContext.Open("FixUE4MacroIndents", false); 89 | } 90 | try { 91 | for (int onLine = startPasteLine; onLine < afterLines.Length; onLine++) { 92 | var line = afterLines[onLine]; 93 | // We only need to potentially fix the newly pasted lines 94 | if (onLine >= startPasteLine + pasteLength) { 95 | sel.GotoLine(onLine, false); 96 | sel.MoveToPoint(editPoint); 97 | break; 98 | } 99 | if (underMacro) { 100 | var varMatch = Regex.Match(line, TEXT_WITH_WS_REGEX); 101 | if (varMatch.Success && varMatch.Groups["leading_whitespace"].ToString() != lastWhiteSpace) { 102 | sel.GotoLine(onLine + 1, false); 103 | sel.DeleteWhitespace(EnvDTE.vsWhitespaceOptions.vsWhitespaceOptionsHorizontal); 104 | sel.Insert(lastWhiteSpace); 105 | underMacro = false; 106 | // This line has been modified, so change it for the MACRO_REGEX matching below 107 | line = lastWhiteSpace + line.Trim(); 108 | } 109 | } 110 | // Its important to check if the current line is a macro 111 | // even if we just fixed the spacing of the current line 112 | var macroMatch = Regex.Match(line, MACRO_REGEX); 113 | if (macroMatch.Success) { 114 | underMacro = true; 115 | lastWhiteSpace = macroMatch.Groups["leading_whitespace"].ToString(); 116 | } else if (Regex.Match(line, @"\S+").Success) { 117 | underMacro = false; 118 | } 119 | } 120 | } finally { 121 | if (openedUndoContext) { 122 | _DTE.UndoContext.Close(); 123 | } 124 | } 125 | } 126 | 127 | private void AfterKeyPress(string key, TextSelection sel, bool completion) { 128 | // Only semicolons or carriage returns should cause an indentation that need to be fixed 129 | if (key != ";" && key != "\r") { 130 | return; 131 | } 132 | // Make sure we're using smart indent 133 | EnvDTE.Properties textEditorC = _DTE.get_Properties("TextEditor", "C/C++"); 134 | if ((int)textEditorC.Item("IndentStyle").Value != 2) { 135 | return; 136 | } 137 | 138 | var doc = _DTE.ActiveDocument; 139 | var editPoint = sel.ActivePoint.CreateEditPoint(); 140 | string macroLine = null; 141 | var macroLineNum = sel.ActivePoint.Line; 142 | // Search up the document from our current line until we find a line with text on it 143 | // so we can then determine if this line contains a macro we care about 144 | var found = false; 145 | while (--macroLineNum >= 1) { 146 | macroLine = editPoint.GetLines(macroLineNum, macroLineNum + 1); 147 | if (!Regex.IsMatch(macroLine, @"\S+")) { 148 | continue; 149 | } 150 | found = true; 151 | break; 152 | } 153 | if (!found) { 154 | return; 155 | } 156 | var macroMatch = Regex.Match(macroLine, MACRO_REGEX); 157 | if (macroMatch.Success) { 158 | // Goto and select our current line, can't do this in a single GotoLine call for some reason 159 | sel.GotoLine(sel.ActivePoint.Line, false); 160 | sel.SelectLine(); 161 | if (Regex.IsMatch(sel.Text, @"\S+")) { 162 | // If the line below the macro has text, undo the indent it just did 163 | doc.Undo(); 164 | } else { 165 | // If the line below the macro is empty, add matching whitespace to the beginning of this line to match it up with the macro 166 | sel.MoveToPoint(editPoint); 167 | sel.DeleteWhitespace(EnvDTE.vsWhitespaceOptions.vsWhitespaceOptionsHorizontal); 168 | sel.Insert(macroMatch.Groups["leading_whitespace"].ToString()); 169 | } 170 | } 171 | } 172 | } 173 | 174 | Extension 175 | CS 176 | v4.0 177 | false 178 | false 179 | 180 | 181 | 182 | --------------------------------------------------------------------------------