Sfall-ScriptEditor
372 строки · 17.0 Кб
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Text;
6using System.Text.RegularExpressions;
7using System.Windows.Forms;
8
9using ICSharpCode.TextEditor;
10using ICSharpCode.TextEditor.Document;
11
12using ScriptEditor.CodeTranslation;
13using ScriptEditor.TextEditorUI;
14
15namespace ScriptEditor.TextEditorUtilities
16{
17internal sealed class Refactor
18{
19internal static void Rename(IParserInfo item, IDocument document, TabInfo cTab, List<TabInfo> tabs)
20{
21string newName;
22switch ((NameType)item.Type())
23{
24case NameType.LVar: // local procedure variable
25Variable lvar = (Variable)item;
26newName = lvar.name;
27if (!ProcForm.CreateRenameForm(ref newName, "Local Variable") || newName == lvar.name)
28return;
29if (cTab.parseInfo.CheckExistsName(newName, NameType.LVar, lvar.fdeclared, lvar.d.declared)) {
30MessageBox.Show("The local variable this name already exists.", "Unable to rename");
31return;
32}
33RenameVariable(lvar, newName, RegexOptions.IgnoreCase, document); // rename only via references
34break;
35
36case NameType.GVar: // script variable
37Variable gvar = (Variable)item;
38newName = gvar.name;
39if (!ProcForm.CreateRenameForm(ref newName, "Script Variable") || newName == gvar.name)
40return;
41if (cTab.parseInfo.CheckExistsName(newName, NameType.GVar)) {
42MessageBox.Show("The variable/procedure or declared macro this name already exists.", "Unable to rename");
43return;
44}
45RenameVariable(gvar, newName, RegexOptions.IgnoreCase, document); // rename only via references
46break;
47
48case NameType.Proc:
49RenameProcedure((Procedure)item, document, cTab, tabs);
50return;
51
52case NameType.Macro:
53Macro macros = (Macro)item;
54
55bool isGlobal = ProgramInfo.macrosGlobal.ContainsKey(macros.token);
56newName = macros.token;
57
58if (!ProcForm.CreateRenameForm(ref newName, (isGlobal) ? "Global Macro" : "Local Macro") || newName == macros.token)
59return;
60
61if (cTab.parseInfo.CheckExistsName(newName, NameType.Macro)) {
62MessageBox.Show("The variable/procedure or declared macro this name already exists.", "Unable to rename");
63return;
64}
65int diff = newName.Length - macros.token.Length;
66
67// Для глобальных требуется переименовать все макросы во всех открытых вкладках и во всех файлах проекта/мода
68if (isGlobal) {
69RenameGlobalMacros(macros, newName, cTab, tabs, diff);
70// обновить макросы
71GetMacros.GetGlobalMacros(Settings.pathHeadersFiles);
72return;
73}
74RenameMacros(macros.token, newName, RegexOptions.None, document);
75if (diff != 0) DefineMacroAdjustSpaces(macros, document, diff);
76break;
77}
78}
79
80#region Переименование макросов
81
82private static void RenameMacros(string find, string newName, RegexOptions option, IDocument document)
83{
84document.UndoStack.StartUndoGroup();
85
86int offset = 0;
87while (offset < document.TextLength)
88{
89offset = Utilities.SearchWholeWord(document.TextContent, find, offset, option);
90if (offset == -1)
91break;
92document.Replace(offset, find.Length, newName);
93offset += newName.Length;
94}
95document.UndoStack.EndUndoGroup();
96}
97
98public struct PreviewMatch
99{
100public string previewText;
101public Match match;
102public TabInfo tab;
103
104public PreviewMatch(string previewText, Match match, TabInfo tab = null)
105{
106this.previewText = previewText;
107this.match = match;
108this.tab = tab;
109}
110
111public PreviewMatch(PreviewMatch obj)
112{
113this.previewText = obj.previewText;
114this.match = obj.match;
115this.tab = obj.tab;
116}
117}
118
119private static void MatchesCollect(ref Dictionary<string, List<PreviewMatch>> preview, Regex regex, string filename, string textContent, TabInfo tab = null)
120{
121MatchCollection matches = regex.Matches(textContent);
122
123if (matches.Count > 0) {
124List<PreviewMatch> list = new List<PreviewMatch>();
125foreach (Match match in matches)
126{
127int pEnd = 0;
128for (int i = match.Index + match.Length; i < textContent.Length; i++)
129{
130if (textContent[i] == '\n') {
131pEnd = i;
132break;
133}
134}
135string previewText = textContent.Substring(match.Index, pEnd - match.Index).TrimEnd();
136list.Add(new PreviewMatch(previewText, match, tab));
137}
138preview.Add(filename ?? tab.filepath, list);
139}
140}
141
142private static Dictionary<string, List<PreviewMatch>> PreviewRenameGlobalMacros(Regex regex, List<TabInfo> tabs)
143{
144Dictionary<string, List<PreviewMatch>> preview = new Dictionary<string, List<PreviewMatch>>(); // file => mathes
145
146foreach (var tab in tabs)
147{
148string textContent = tab.textEditor.Document.TextContent;
149MatchesCollect(ref preview, regex, null, textContent, tab);
150}
151return preview;
152}
153
154private static Dictionary<string, List<PreviewMatch>> PreviewRenameGlobalMacros(Regex regex, List<TabInfo> tabs, List<string> files)
155{
156ProgressBarForm pf = (files.Count > 100) ? new ProgressBarForm(Form.ActiveForm, files.Count, "Идет поиск совпадений в файлах проекта...") : null;
157Dictionary<string, List<PreviewMatch>> preview = new Dictionary<string, List<PreviewMatch>>(); // file => mathes
158
159foreach (var file in files)
160{
161if (pf != null) pf.IncProgress();
162if (TextEditor.CheckTabs(file, tabs)) continue; // next file
163
164string textContent = System.IO.File.ReadAllText(file);
165MatchesCollect(ref preview, regex, file, textContent);
166}
167if (pf != null) pf.Dispose();
168return preview;
169}
170
171private static void RenameGlobalMacros(Macro macros, string newName, TabInfo cTab, List<TabInfo> tabs, int diff)
172{
173Regex s_regex = new Regex(@"\b" + macros.token + @"\b", RegexOptions.None);
174
175// preview renamed macros open scripts
176Dictionary<string, List<PreviewMatch>> matchTabs = PreviewRenameGlobalMacros(s_regex, tabs); // file => mathes
177
178List<string> files = Directory.GetFiles(Settings.solutionProjectFolder, "*.ssl", SearchOption.AllDirectories).ToList();
179files.AddRange(Directory.GetFiles(Settings.solutionProjectFolder, "*.h", SearchOption.AllDirectories).ToList());
180
181// preview renamed macros open scripts
182Dictionary<string, List<PreviewMatch>> matchFiles = PreviewRenameGlobalMacros(s_regex, tabs, files);
183
184// выбор совпадений
185var previewForm = new PreviewRename(macros.defname, macros.defname.Replace(macros.token, newName));
186previewForm.BuildTreeMatches(matchTabs, matchFiles);
187
188matchTabs.Clear();
189matchFiles.Clear();
190
191if (previewForm.ShowDialog() != DialogResult.OK) return;
192
193previewForm.GetSelectedMatches(ref matchTabs, ref matchFiles);
194previewForm.Dispose();
195
196///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
197
198int isAdjustSpaces = diff;
199cTab.DisableParseAndStatusChange = true;
200
201int replaceLen = macros.token.Length;
202
203foreach (var tabMatches in matchTabs)
204{
205TabInfo tab = null;
206int replace_count = 0;
207foreach (var matches in tabMatches.Value)
208{
209tab = matches.tab;
210Match match = matches.match;
211int offset = (diff * replace_count) + match.Index;
212tab.textEditor.Document.Replace(offset, replaceLen, newName);
213replace_count++;
214}
215if (tab != null) {
216var document = tab.textEditor.Document;
217if (isAdjustSpaces != 0 && string.Equals(tab.filepath, macros.fdeclared, StringComparison.OrdinalIgnoreCase)) {
218DefineMacroAdjustSpaces(macros, document, diff);
219isAdjustSpaces = 0;
220}
221document.UndoStack.ClearAll();
222tab.SaveInternal(document.TextContent, tab.textEditor.Encoding); // сохранить изменения в файл
223}
224}
225
226if (matchFiles.Count == 0) {
227cTab.DisableParseAndStatusChange = false;
228return;
229}
230// замена в файлах проекта
231ProgressBarForm pf = (matchFiles.Count > 100) ? new ProgressBarForm(Form.ActiveForm, matchFiles.Count, "Идет замена в файлах проекта...") : null;
232
233int total = 0;
234foreach (var fileMatches in matchFiles)
235{
236string textContent = System.IO.File.ReadAllText(fileMatches.Key);
237total += fileMatches.Value.Count;
238
239int replace_count = 0;
240foreach (var matches in fileMatches.Value)
241{
242int offset = (diff * replace_count) + matches.match.Index;
243textContent = textContent.Remove(offset, matches.match.Length);
244textContent = textContent.Insert(offset, newName);
245replace_count++;
246}
247if (isAdjustSpaces != 0 && string.Equals(fileMatches.Key, macros.fdeclared, StringComparison.OrdinalIgnoreCase)) {
248DefineMacroAdjustSpaces(macros, newName, ref textContent, diff);
249isAdjustSpaces = 0;
250}
251if (replace_count > 0) {
252File.WriteAllText(fileMatches.Key, textContent, (Settings.saveScriptUTF8) ? new UTF8Encoding(false) : Encoding.Default);
253}
254if (pf != null) pf.IncProgress();
255}
256if (pf != null) pf.Dispose();
257
258//MessageBox.Show(String.Format("Произведено переименование {0} макросов, в {1} файлах.", total, previewFiles.Count));
259cTab.DisableParseAndStatusChange = false;
260}
261
262// insert/delete spaces in define macro
263private static void DefineMacroAdjustSpaces(Macro macros, string newName, ref string textContent, int diff)
264{
265int offset = 0;
266while (true)
267{
268offset = textContent.IndexOf(" " + newName, offset); // ищем первое определение
269if (offset == -1 || offset >= textContent.Length) return;
270
271char c = textContent[offset + newName.Length + 1];
272if (c != '(' && !char.IsWhiteSpace(c)) continue; // перейти к следующему поиску если имя определения макроса не заканчивается символами пробела или открыващей скобкой
273
274if (textContent.IndexOf("#define", offset - 7, 7) != -1) break; // да, это строка определения макроса
275}
276offset += (macros.defname.Length + diff) + 1;
277
278if (diff > 0) {
279int removeCount = 0;
280for (int i = 0; i < diff; i++)
281{
282if (!Char.IsWhiteSpace(textContent[offset + i + 1])) break;
283removeCount++;
284}
285if (removeCount > 0) textContent = textContent.Remove(offset, removeCount);
286} else
287textContent = textContent.Insert(offset, new string(' ', Math.Abs(diff)));
288}
289
290// insert/delete spaces in define macro
291private static void DefineMacroAdjustSpaces(Macro macros, IDocument document, int diff)
292{
293int offset = document.PositionToOffset(new TextLocation(0, macros.declared - 1));
294offset += (macros.defname.Length + 8) + diff;
295
296if (diff > 0) {
297int removeCount = 0;
298for (int i = 0; i < diff; i++)
299{
300if (!Char.IsWhiteSpace(document.GetCharAt(offset + i + 1))) break;
301removeCount++;
302}
303if (removeCount > 0) document.Remove(offset, removeCount);
304} else
305document.Insert(offset, new string(' ', Math.Abs(diff)));
306}
307#endregion
308
309private static void RenameVariable(Variable var, string newName, RegexOptions option, IDocument document)
310{
311Utilities.ReplaceByReferences(new Regex(@"\b" + var.name + @"\b", option), document, var, newName, newName.Length - var.name.Length);
312}
313
314// вызывается из редактора нодов диалога
315internal static string RenameProcedure(string name, IDocument document, TabInfo cTab)
316{
317Procedure proc = cTab.parseInfo.GetProcedureByName(name);
318return (proc != null) ? RenameProcedure(proc, document, cTab) : null;
319}
320
321// Search and replace procedure name in script text
322internal static string RenameProcedure(Procedure proc, IDocument document, TabInfo cTab, List<TabInfo> tabs = null)
323{
324string newName = proc.name;
325// form ini
326if (!ProcForm.CreateRenameForm(ref newName, "Procedure") || newName == proc.name)
327return null;
328
329if (cTab.parseInfo.CheckExistsName(newName, NameType.Proc)) {
330MessageBox.Show("The procedure/variable or declared macro with this name already exists.", "Unable to rename");
331return null;
332}
333
334bool extFile = false;
335if (tabs != null && proc.filename != cTab.filename.ToLowerInvariant()) { // не совсем понятно, при каких условиях это верно
336switch (MessageBox.Show("Also renaming the procedure in a file: " + proc.filename.ToUpper() + " ?", "Procedure rename", MessageBoxButtons.YesNoCancel)) {
337case DialogResult.Cancel :
338return null;
339case DialogResult.Yes :
340extFile = true;
341break;
342}
343}
344int differ = newName.Length - proc.name.Length;
345
346Regex s_regex;
347if (proc.References().Length == 0) {
348s_regex = new Regex(@"(\bprocedure\s|\bcall\s|[=,(]\s*)\s*" + proc.Name + @"\b", // осуществить поиск всех процедур совпадающих по имени
349RegexOptions.Multiline | RegexOptions.IgnoreCase);
350Utilities.ReplaceIDocumentText(s_regex, document, newName, differ);
351} else {
352s_regex = new Regex(@"\b" + proc.Name + @"\b", RegexOptions.Multiline | RegexOptions.IgnoreCase);
353Utilities.ReplaceByReferences(s_regex, document, proc, newName, differ); // rename by reference
354}
355
356// rename in other file/tab
357if (extFile) {
358string text = File.ReadAllText(proc.fstart);
359Utilities.ReplaceSpecialText(s_regex, ref text, newName, differ);
360File.WriteAllText(proc.fstart, text);
361
362TabInfo tab = TextEditor.CheckTabs(tabs, proc.fstart);
363if (tab != null) {
364Utilities.ReplaceIDocumentText(s_regex, tab.textEditor.Document, newName, differ);
365tab.FileTime = File.GetLastWriteTime(proc.fstart);
366}
367}
368TextEditor.currentHighlightProc = null;
369return newName;
370}
371}
372}
373