// Here is the definition of a line edit public interface ILineEdit { string ApplyEdit(string input); } /// <summary> /// Perform a bunch of edits on a text file or string on a line by line basis. /// Only search and replace type edits are available. /// </summary> /// <summary> /// Perform a bunch of edits on a IEnumerable<string>. /// </summary> public class TextLineEditor { private List<ILineEdit> _pendingEdits = new List<ILineEdit>(); public TextLineEditor() { } public IEnumerable<string> ApplyEdits(IEnumerable<string> inputs) { foreach (var line in inputs) { string tmp = line; foreach (ILineEdit edit in _pendingEdits) { tmp = edit.ApplyEdit(tmp); } yield return tmp; } } public void AddEdit(ILineEdit edit) { Trace.Assert(edit != null); _pendingEdits.Add(edit); } }
We can use this as the basis of a line by line text file ediitor:
/// <summary> /// Perform a bunch of edits on a text file /// Only search and replace type edits are available. /// </summary> public class FileTextLineEditor : TextLineEditor { public void ApplyEditsToFile(string inputPath, bool keepOriginal = true) { Debug.Assert(!string.IsNullOrWhiteSpace(inputPath)); var inputFileInfo = new FileInfo(inputPath); Debug.Assert(inputFileInfo.Exists); var outputPath = inputPath + ".new"; var outputFileInfo = new FileInfo(outputPath); Debug.Assert(!outputFileInfo.Exists); File.WriteAllLines(outputFileInfo.FullName, ApplyEdits(File.ReadLines(inputFileInfo.FullName))); inputFileInfo.MoveTo(inputPath + ".bak"); outputFileInfo.Refresh(); outputFileInfo.MoveTo(inputPath); if (!keepOriginal) { File.Delete(inputPath + ".bak"); } } }Here is a LineEdit example. Note that it can do deletes by replacing with a blank string.
public class SearchAndReplace : ILineEdit { public enum RegExUsage { None = 0, Use } public SearchAndReplace() { } public SearchAndReplace(string searchFor, string replaceWith, bool extendedChars = false, RegExUsage regExUse = RegExUsage.None) { if (string.IsNullOrEmpty(searchFor)) throw new ArgumentException("Cannot be null or an empty string", "searchFor"); if (replaceWith == null) throw new ArgumentException("Cannot be null", "replaceWith"); this.SearchFor = searchFor; this.ReplaceWith = replaceWith; this.ExtendedChars = extendedChars; } public bool IsRegEx { get; set; } = false; public string SearchFor { get; set; } = ""; public string ReplaceWith { get; set; } = ""; public bool ExtendedChars { get; set; } = false; public string ApplyEdit(string input) { if (ExtendedChars) { SearchFor = SearchFor.Replace("\\t","\t").Replace("\\n","\n").Replace("\\r","\r"); } return (IsRegEx) ? SearchReplaceInString(input) : SearchReplaceInStringRegEx(input); } private string SearchReplaceInString(string input) { string output = input.Replace(SearchFor, ReplaceWith); return output; } private string SearchReplaceInStringRegEx(string input) { string output = Regex.Replace(input, SearchFor, ReplaceWith); return output; } }Here is an example usage that attempts to replace usage of Moq in a CS file to usage of NSubstitute:
void Main() { string inputFile = @"a:/path/SomeFileName.cs"; FileTextLineEditor resEditor = new(); resEditor.AddEdits(this.Edits()); resEditor.ApplyEditsToFile(inputFile, keepOriginal: false); } internal IEnumerable<ILineEdit> Edits() { yield return new SearchAndReplace("using Moq", "using NSubstitute"); yield return new SearchAndReplace("MockBehavior.Strict", ""); yield return new SearchAndReplace("MockBehavior.Loose", ""); yield return new SearchAndReplace(".Object", ""); yield return new SearchAndReplace("It.IsAny", "Arg.Any"); yield return new SearchAndReplace("It.Is", "Arg.Is"); yield return new SearchAndReplace(@"(new\sMock\<([A-Za-z\<\>\-_]+)\>)", @"Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use); yield return new SearchAndReplace(@"(Mock\<([A-Za-z\<\>\-_]+)\>\s([A-Za-z\<\>\-_]+)\s\=\snew)", @"var $3 = Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use); // yield return new SearchAndReplace(@" = new()", @" = Substitute.For(??)"); //yield return new SearchAndReplace(@"(^\sMock\<([A-Za-z\<\>\-_]+)", "var", false, SearchAndReplace.RegExUsage.Use); yield return new SearchAndReplace(@"(Setup\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use); yield return new SearchAndReplace(@"(SetupGet\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use); yield return new SearchAndReplace(@"(Verify\([a-z]+\s\=\>\s[a-z]+)", "Received(?)", false, SearchAndReplace.RegExUsage.Use); yield return new SearchAndReplace("Times.Once", "1"); yield return new SearchAndReplace("Times.Never", "0"); yield return new SearchAndReplace(@"Returns\(\(\)\s\=\>\s", "Returns("); }
No comments:
Post a Comment