From 2c7cb2b69ccfc3a728319f62e61f8f4d36b7f355 Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Mon, 7 Jul 2025 18:37:46 +1000 Subject: [PATCH] refactor: streamline certain string operations --- src/App.axaml.cs | 2 +- src/Commands/Blame.cs | 2 +- src/Commands/Config.cs | 10 +++------- src/Commands/Diff.cs | 15 +++----------- src/Commands/QueryBranches.cs | 8 ++++---- src/Commands/QueryRevisionObjects.cs | 14 +++---------- src/Commands/Statistics.cs | 26 +++++++------------------ src/Models/Change.cs | 23 +++++++--------------- src/Models/OpenAI.cs | 8 ++++---- src/Models/Stash.cs | 3 +-- src/Models/Statistics.cs | 2 +- src/Models/User.cs | 11 ++++++----- src/ViewModels/BranchTreeNode.cs | 2 +- src/ViewModels/ChangeTreeNode.cs | 2 +- src/ViewModels/CommitDetail.cs | 2 +- src/ViewModels/FileHistories.cs | 5 +++-- src/ViewModels/InteractiveRebase.cs | 10 +++------- src/ViewModels/PushRevision.cs | 5 +++-- src/ViewModels/SubmoduleCollection.cs | 2 +- src/ViewModels/TagCollection.cs | 2 +- src/ViewModels/WorkingCopy.cs | 5 ++--- src/Views/CommitMessageEditor.axaml.cs | 16 +++++++-------- src/Views/CommitMessageTextBox.axaml.cs | 24 +++++++++-------------- 23 files changed, 74 insertions(+), 125 deletions(-) diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 53c25325f..24f034d52 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -695,7 +695,7 @@ private string FixFontFamilyName(string input) } var name = sb.ToString(); - if (name.Contains('#', StringComparison.Ordinal)) + if (name.Contains('#')) { if (!name.Equals("fonts:Inter#Inter", StringComparison.Ordinal) && !name.Equals("fonts:SourceGit#JetBrains Mono", StringComparison.Ordinal)) diff --git a/src/Commands/Blame.cs b/src/Commands/Blame.cs index 0d55a051c..bd2c6df84 100644 --- a/src/Commands/Blame.cs +++ b/src/Commands/Blame.cs @@ -50,7 +50,7 @@ public Blame(string repo, string file, string revision) private void ParseLine(string line) { - if (line.Contains('\0', StringComparison.Ordinal)) + if (line.Contains('\0')) { _result.IsBinary = true; _result.LineInfos.Clear(); diff --git a/src/Commands/Config.cs b/src/Commands/Config.cs index 78057b222..84858454a 100644 --- a/src/Commands/Config.cs +++ b/src/Commands/Config.cs @@ -31,13 +31,9 @@ public async Task> ReadAllAsync() var lines = output.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { - var idx = line.IndexOf('=', StringComparison.Ordinal); - if (idx != -1) - { - var key = line.Substring(0, idx).Trim(); - var val = line.Substring(idx + 1).Trim(); - rs[key] = val; - } + var parts = line.Split('=', 2); + if (parts.Length == 2) + rs[parts[0]] = parts[1]; } } diff --git a/src/Commands/Diff.cs b/src/Commands/Diff.cs index c4785e18f..3eae1b54b 100644 --- a/src/Commands/Diff.cs +++ b/src/Commands/Diff.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -39,20 +40,10 @@ public Diff(string repo, Models.DiffOption opt, int unified, bool ignoreWhitespa public async Task ReadAsync() { var rs = await ReadToEndAsync().ConfigureAwait(false); - var start = 0; - var end = rs.StdOut.IndexOf('\n', start); - while (end > 0) - { - var line = rs.StdOut.Substring(start, end - start); + var sr = new StringReader(rs.StdOut); + while (sr.ReadLine() is { } line) ParseLine(line); - start = end + 1; - end = rs.StdOut.IndexOf('\n', start); - } - - if (start < rs.StdOut.Length) - ParseLine(rs.StdOut.Substring(start)); - if (_result.IsBinary || _result.IsLFS || _result.TextDiff.Lines.Count == 0) { _result.TextDiff = null; diff --git a/src/Commands/QueryBranches.cs b/src/Commands/QueryBranches.cs index e8a8372b6..0eaf2837f 100644 --- a/src/Commands/QueryBranches.cs +++ b/src/Commands/QueryBranches.cs @@ -80,12 +80,12 @@ private Models.Branch ParseLine(string line) else if (refName.StartsWith(PREFIX_REMOTE, StringComparison.Ordinal)) { var name = refName.Substring(PREFIX_REMOTE.Length); - var shortNameIdx = name.IndexOf('/', StringComparison.Ordinal); - if (shortNameIdx < 0) + var nameParts = name.Split('/', 2); + if (nameParts.Length != 2) return null; - branch.Remote = name.Substring(0, shortNameIdx); - branch.Name = name.Substring(branch.Remote.Length + 1); + branch.Remote = nameParts[0]; + branch.Name = nameParts[1]; branch.IsLocal = false; } else diff --git a/src/Commands/QueryRevisionObjects.cs b/src/Commands/QueryRevisionObjects.cs index e991cd0cd..c73932fb2 100644 --- a/src/Commands/QueryRevisionObjects.cs +++ b/src/Commands/QueryRevisionObjects.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -25,18 +26,9 @@ public QueryRevisionObjects(string repo, string sha, string parentFolder) var rs = await ReadToEndAsync().ConfigureAwait(false); if (rs.IsSuccess) { - var start = 0; - var end = rs.StdOut.IndexOf('\0', start); - while (end > 0) - { - var line = rs.StdOut.Substring(start, end - start); + var sr = new StringReader(rs.StdOut); + while (sr.ReadLine() is { } line) Parse(outs, line); - start = end + 1; - end = rs.StdOut.IndexOf('\0', start); - } - - if (start < rs.StdOut.Length) - Parse(outs, rs.StdOut.Substring(start)); } return outs; diff --git a/src/Commands/Statistics.cs b/src/Commands/Statistics.cs index 513b96403..2d43c7629 100644 --- a/src/Commands/Statistics.cs +++ b/src/Commands/Statistics.cs @@ -1,4 +1,4 @@ -using System; +using System.IO; using System.Threading.Tasks; namespace SourceGit.Commands @@ -19,17 +19,9 @@ public Statistics(string repo, int max) if (!rs.IsSuccess) return statistics; - var start = 0; - var end = rs.StdOut.IndexOf('\n', start); - while (end > 0) - { - ParseLine(statistics, rs.StdOut.Substring(start, end - start)); - start = end + 1; - end = rs.StdOut.IndexOf('\n', start); - } - - if (start < rs.StdOut.Length) - ParseLine(statistics, rs.StdOut.Substring(start)); + var sr = new StringReader(rs.StdOut); + while (sr.ReadLine() is { } line) + ParseLine(statistics, line); statistics.Complete(); return statistics; @@ -37,13 +29,9 @@ public Statistics(string repo, int max) private void ParseLine(Models.Statistics statistics, string line) { - var dateEndIdx = line.IndexOf('$', StringComparison.Ordinal); - if (dateEndIdx == -1) - return; - - var dateStr = line.AsSpan(0, dateEndIdx); - if (double.TryParse(dateStr, out var date)) - statistics.AddCommit(line.Substring(dateEndIdx + 1), date); + var parts = line.Split('$', 2); + if (parts.Length == 2 && double.TryParse(parts[0], out var date)) + statistics.AddCommit(parts[1], date); } } } diff --git a/src/Models/Change.cs b/src/Models/Change.cs index 129678be6..2ad448add 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -1,6 +1,4 @@ -using System; - -namespace SourceGit.Models +namespace SourceGit.Models { public enum ChangeViewMode { @@ -64,20 +62,13 @@ public void Set(ChangeState index, ChangeState workTree = ChangeState.None) if (index == ChangeState.Renamed || workTree == ChangeState.Renamed) { - var idx = Path.IndexOf('\t', StringComparison.Ordinal); - if (idx >= 0) - { - OriginalPath = Path.Substring(0, idx); - Path = Path.Substring(idx + 1); - } - else + var parts = Path.Split('\t', 2); + if (parts.Length < 2) + parts = Path.Split(" -> ", 2); + if (parts.Length == 2) { - idx = Path.IndexOf(" -> ", StringComparison.Ordinal); - if (idx > 0) - { - OriginalPath = Path.Substring(0, idx); - Path = Path.Substring(idx + 4); - } + OriginalPath = parts[0]; + Path = parts[1]; } } diff --git a/src/Models/OpenAI.cs b/src/Models/OpenAI.cs index 9ca3506ed..70b319c65 100644 --- a/src/Models/OpenAI.cs +++ b/src/Models/OpenAI.cs @@ -32,17 +32,17 @@ public void Append(string text) buffer = REG_COT().Replace(buffer, ""); - var startIdx = buffer.IndexOf('<', StringComparison.Ordinal); + var startIdx = buffer.IndexOf('<'); if (startIdx >= 0) { if (startIdx > 0) OnReceive(buffer.Substring(0, startIdx)); - var endIdx = buffer.IndexOf(">", startIdx + 1, StringComparison.Ordinal); + var endIdx = buffer.IndexOf('>', startIdx + 1); if (endIdx <= startIdx) { if (buffer.Length - startIdx <= 15) - _thinkTail.Append(buffer.Substring(startIdx)); + _thinkTail.Append(buffer.AsSpan(startIdx)); else OnReceive(buffer.Substring(startIdx)); } @@ -50,7 +50,7 @@ public void Append(string text) { var tag = buffer.Substring(startIdx + 1, endIdx - startIdx - 1); if (_thinkTags.Contains(tag)) - _thinkTail.Append(buffer.Substring(startIdx)); + _thinkTail.Append(buffer.AsSpan(startIdx)); else OnReceive(buffer.Substring(startIdx)); } diff --git a/src/Models/Stash.cs b/src/Models/Stash.cs index 2b77be50a..bc01e9db1 100644 --- a/src/Models/Stash.cs +++ b/src/Models/Stash.cs @@ -15,8 +15,7 @@ public string Subject { get { - var idx = Message.IndexOf('\n', StringComparison.Ordinal); - return idx > 0 ? Message.Substring(0, idx).Trim() : Message; + return Message.Split('\n', 2)[0].Trim(); } } diff --git a/src/Models/Statistics.cs b/src/Models/Statistics.cs index a86380c3d..700c9311b 100644 --- a/src/Models/Statistics.cs +++ b/src/Models/Statistics.cs @@ -199,7 +199,7 @@ public Statistics() public void AddCommit(string author, double timestamp) { - var emailIdx = author.IndexOf('±', StringComparison.Ordinal); + var emailIdx = author.IndexOf('±'); var email = author.Substring(emailIdx + 1).ToLower(CultureInfo.CurrentCulture); if (!_users.TryGetValue(email, out var user)) { diff --git a/src/Models/User.cs b/src/Models/User.cs index 0b4816feb..d3faedf0d 100644 --- a/src/Models/User.cs +++ b/src/Models/User.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; namespace SourceGit.Models { @@ -17,10 +16,12 @@ public User() public User(string data) { - var nameEndIdx = data.IndexOf('±', StringComparison.Ordinal); + var parts = data.Split('±', 2); + if (parts.Length < 2) + parts = [string.Empty, data]; - Name = nameEndIdx > 0 ? data.Substring(0, nameEndIdx) : string.Empty; - Email = data.Substring(nameEndIdx + 1); + Name = parts[0]; + Email = parts[1]; _hash = data.GetHashCode(); } diff --git a/src/ViewModels/BranchTreeNode.cs b/src/ViewModels/BranchTreeNode.cs index 1550e8076..5c5d2de1d 100644 --- a/src/ViewModels/BranchTreeNode.cs +++ b/src/ViewModels/BranchTreeNode.cs @@ -151,7 +151,7 @@ private void MakeBranchNode(Models.Branch branch, List roots, Di { var time = branch.CommitterDate; var fullpath = $"{prefix}/{branch.Name}"; - var sepIdx = branch.Name.IndexOf('/', StringComparison.Ordinal); + var sepIdx = branch.Name.IndexOf('/'); if (sepIdx == -1 || branch.IsDetachedHead) { roots.Add(new BranchTreeNode() diff --git a/src/ViewModels/ChangeTreeNode.cs b/src/ViewModels/ChangeTreeNode.cs index 6c061f658..74302eea9 100644 --- a/src/ViewModels/ChangeTreeNode.cs +++ b/src/ViewModels/ChangeTreeNode.cs @@ -55,7 +55,7 @@ public static List Build(IList changes, HashSet 0) - Subject = normalized.Substring(0, idx).ReplaceLineEndings(" "); - else - Subject = value.ReplaceLineEndings(" "); + var parts = normalized.Split("\n\n", 2); + Subject = parts[0].ReplaceLineEndings(" "); } } } diff --git a/src/ViewModels/PushRevision.cs b/src/ViewModels/PushRevision.cs index a257aea88..491488c4b 100644 --- a/src/ViewModels/PushRevision.cs +++ b/src/ViewModels/PushRevision.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace SourceGit.ViewModels { @@ -31,7 +32,7 @@ public PushRevision(Repository repo, Models.Commit revision, Models.Branch remot public override async Task Sure() { _repo.SetWatcherEnabled(false); - ProgressDescription = $"Push {Revision.SHA.Substring(0, 10)} -> {RemoteBranch.FriendlyName} ..."; + ProgressDescription = $"Push {Revision.SHA.AsSpan(0, 10)} -> {RemoteBranch.FriendlyName} ..."; var log = _repo.CreateLog("Push Revision"); Use(log); diff --git a/src/ViewModels/SubmoduleCollection.cs b/src/ViewModels/SubmoduleCollection.cs index b36883660..6c849fdb1 100644 --- a/src/ViewModels/SubmoduleCollection.cs +++ b/src/ViewModels/SubmoduleCollection.cs @@ -59,7 +59,7 @@ public static List Build(IList submodules, foreach (var module in submodules) { - var sepIdx = module.Path.IndexOf('/', StringComparison.Ordinal); + var sepIdx = module.Path.IndexOf('/'); if (sepIdx == -1) { nodes.Add(new SubmoduleTreeNode(module, 0)); diff --git a/src/ViewModels/TagCollection.cs b/src/ViewModels/TagCollection.cs index 0f2cc5af0..34fd2b928 100644 --- a/src/ViewModels/TagCollection.cs +++ b/src/ViewModels/TagCollection.cs @@ -70,7 +70,7 @@ public static List Build(List tags, HashSet exp foreach (var tag in tags) { - var sepIdx = tag.Name.IndexOf('/', StringComparison.Ordinal); + var sepIdx = tag.Name.IndexOf('/'); if (sepIdx == -1) { nodes.Add(new TagTreeNode(tag, 0)); diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index 6ab8f8d70..3328ef610 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -774,7 +774,7 @@ public ContextMenu CreateContextMenuForUnstagedChanges(string selectedSingleFold } else { - var isRooted = change.Path.IndexOf('/', StringComparison.Ordinal) <= 0; + var isRooted = change.Path.IndexOf('/') <= 0; var singleFile = new MenuItem(); singleFile.Header = App.Text("WorkingCopy.AddToGitIgnore.SingleFile"); singleFile.Click += (_, e) => @@ -1654,8 +1654,7 @@ public ContextMenu CreateContextMenuForCommitMessages() for (int i = 0; i < historiesCount; i++) { var message = _repo.Settings.CommitMessages[i].Trim().ReplaceLineEndings("\n"); - var subjectEndIdx = message.IndexOf('\n'); - var subject = subjectEndIdx > 0 ? message.Substring(0, subjectEndIdx) : message; + var subject = message.Split('\n', 2)[0]; var item = new MenuItem(); item.Header = new CommitMessageRecord(subject); item.Icon = App.CreateMenuIcon("Icons.Histories"); diff --git a/src/Views/CommitMessageEditor.axaml.cs b/src/Views/CommitMessageEditor.axaml.cs index a3d894070..1d2c4d46f 100644 --- a/src/Views/CommitMessageEditor.axaml.cs +++ b/src/Views/CommitMessageEditor.axaml.cs @@ -19,15 +19,15 @@ public void AsStandalone(string file) _shouldExitApp = true; var content = File.ReadAllText(file).ReplaceLineEndings("\n").Trim(); - var firstLineEnd = content.IndexOf('\n', StringComparison.Ordinal); - if (firstLineEnd == -1) + var parts = content.Split('\n', 2); + if (parts.Length != 2) { Editor.SubjectEditor.Text = content; } else { - Editor.SubjectEditor.Text = content.Substring(0, firstLineEnd); - Editor.DescriptionEditor.Text = content.Substring(firstLineEnd + 1).Trim(); + Editor.SubjectEditor.Text = parts[0]; + Editor.DescriptionEditor.Text = parts[1]; } } @@ -36,15 +36,15 @@ public void AsBuiltin(string msg, Action onSave) _onSave = onSave; _shouldExitApp = false; - var firstLineEnd = msg.IndexOf('\n', StringComparison.Ordinal); - if (firstLineEnd == -1) + var parts = msg.Split('\n', 2); + if (parts.Length != 2) { Editor.SubjectEditor.Text = msg; } else { - Editor.SubjectEditor.Text = msg.Substring(0, firstLineEnd); - Editor.DescriptionEditor.Text = msg.Substring(firstLineEnd + 1).Trim(); + Editor.SubjectEditor.Text = parts[0]; + Editor.DescriptionEditor.Text = parts[1]; } } diff --git a/src/Views/CommitMessageTextBox.axaml.cs b/src/Views/CommitMessageTextBox.axaml.cs index 0fbf3ab4e..cf08eb6b0 100644 --- a/src/Views/CommitMessageTextBox.axaml.cs +++ b/src/Views/CommitMessageTextBox.axaml.cs @@ -105,17 +105,11 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang { _changingWay = TextChangeWay.FromSource; var normalized = Text.ReplaceLineEndings("\n"); - var subjectEnd = normalized.IndexOf("\n\n", StringComparison.Ordinal); - if (subjectEnd == -1) - { - SetCurrentValue(SubjectProperty, normalized.ReplaceLineEndings(" ")); - SetCurrentValue(DescriptionProperty, string.Empty); - } - else - { - SetCurrentValue(SubjectProperty, normalized.Substring(0, subjectEnd).ReplaceLineEndings(" ")); - SetCurrentValue(DescriptionProperty, normalized.Substring(subjectEnd + 2)); - } + var parts = normalized.Split("\n\n", 2); + if (parts.Length != 2) + parts = [normalized, string.Empty]; + SetCurrentValue(SubjectProperty, parts[0].ReplaceLineEndings(" ")); + SetCurrentValue(DescriptionProperty, parts[1]); _changingWay = TextChangeWay.None; } else if ((change.Property == SubjectProperty || change.Property == DescriptionProperty) && _changingWay == TextChangeWay.None) @@ -145,17 +139,17 @@ private async void OnSubjectTextBoxPreviewKeyDown(object _, KeyEventArgs e) if (SubjectEditor.CaretIndex == Subject.Length) { - var idx = text.IndexOf('\n'); - if (idx == -1) + var parts = text.Split('\n', 2); + if (parts.Length != 2) { SubjectEditor.Paste(text); } else { - SubjectEditor.Paste(text.Substring(0, idx)); + SubjectEditor.Paste(parts[0]); DescriptionEditor.Focus(); DescriptionEditor.CaretIndex = 0; - DescriptionEditor.Paste(text.Substring(idx + 1).Trim()); + DescriptionEditor.Paste(parts[1].Trim()); } } else