diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 50a51207..45830ad0 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -89,20 +89,32 @@ namespace SourceGit public static readonly SimpleCommand OpenPreferenceCommand = new SimpleCommand(() => { + var toplevel = GetTopLevel() as Window; + if (toplevel == null) + return; + var dialog = new Views.Preference(); - dialog.ShowDialog(GetTopLevel() as Window); + dialog.ShowDialog(toplevel); }); public static readonly SimpleCommand OpenHotkeysCommand = new SimpleCommand(() => { + var toplevel = GetTopLevel() as Window; + if (toplevel == null) + return; + var dialog = new Views.Hotkeys(); - dialog.ShowDialog(GetTopLevel() as Window); + dialog.ShowDialog(toplevel); }); public static readonly SimpleCommand OpenAboutCommand = new SimpleCommand(() => { + var toplevel = GetTopLevel() as Window; + if (toplevel == null) + return; + var dialog = new Views.About(); - dialog.ShowDialog(GetTopLevel() as Window); + dialog.ShowDialog(toplevel); }); public static readonly SimpleCommand CheckForUpdateCommand = new SimpleCommand(() => @@ -127,7 +139,7 @@ namespace SourceGit public static void SetLocale(string localeKey) { var app = Current as App; - var targetLocale = app.Resources[localeKey] as ResourceDictionary; + var targetLocale = app?.Resources[localeKey] as ResourceDictionary; if (targetLocale == null || targetLocale == app._activeLocale) return; @@ -141,6 +153,8 @@ namespace SourceGit public static void SetTheme(string theme, string themeOverridesFile) { var app = Current as App; + if (app == null) + return; if (theme.Equals("Light", StringComparison.OrdinalIgnoreCase)) app.RequestedThemeVariant = ThemeVariant.Light; @@ -179,6 +193,7 @@ namespace SourceGit } catch { + // ignore } } else @@ -189,18 +204,18 @@ namespace SourceGit public static async void CopyText(string data) { - if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if (desktop.MainWindow.Clipboard is { } clipbord) + if (desktop.MainWindow?.Clipboard is { } clipbord) await clipbord.SetTextAsync(data); } } public static async Task GetClipboardTextAsync() { - if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if (desktop.MainWindow.Clipboard is { } clipboard) + if (desktop.MainWindow?.Clipboard is { } clipboard) { return await clipboard.GetTextAsync(); } @@ -210,7 +225,7 @@ namespace SourceGit public static string Text(string key, params object[] args) { - var fmt = Current.FindResource($"Text.{key}") as string; + var fmt = Current?.FindResource($"Text.{key}") as string; if (string.IsNullOrWhiteSpace(fmt)) return $"Text.{key}"; @@ -226,16 +241,21 @@ namespace SourceGit icon.Width = 12; icon.Height = 12; icon.Stretch = Stretch.Uniform; - icon.Data = Current.FindResource(key) as StreamGeometry; + + var geo = Current?.FindResource(key) as StreamGeometry; + if (geo != null) + icon.Data = geo; + return icon; } public static TopLevel GetTopLevel() { - if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { return desktop.MainWindow; } + return null; } @@ -297,9 +317,9 @@ namespace SourceGit public static void Quit() { - if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow.Close(); + desktop.MainWindow?.Close(); desktop.Shutdown(); } } @@ -360,7 +380,7 @@ namespace SourceGit { Dispatcher.UIThread.Post(() => { - if (Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: not null } desktop) { var dialog = new Views.SelfUpdate() { @@ -384,11 +404,11 @@ namespace SourceGit if (!filename.Equals("git-rebase-todo", StringComparison.OrdinalIgnoreCase)) return true; - var dirInfo = new DirectoryInfo(Path.GetDirectoryName(file)); + var dirInfo = new DirectoryInfo(Path.GetDirectoryName(file)!); if (!dirInfo.Exists || !dirInfo.Name.Equals("rebase-merge", StringComparison.Ordinal)) return true; - var jobsFile = Path.Combine(dirInfo.Parent.FullName, "sourcegit_rebase_jobs.json"); + var jobsFile = Path.Combine(dirInfo.Parent!.FullName, "sourcegit_rebase_jobs.json"); if (!File.Exists(jobsFile)) return true; @@ -437,16 +457,16 @@ namespace SourceGit if (!filename.Equals("COMMIT_EDITMSG", StringComparison.OrdinalIgnoreCase)) return true; - var jobsFile = Path.Combine(Path.GetDirectoryName(file), "sourcegit_rebase_jobs.json"); + var jobsFile = Path.Combine(Path.GetDirectoryName(file)!, "sourcegit_rebase_jobs.json"); if (!File.Exists(jobsFile)) return true; var collection = JsonSerializer.Deserialize(File.ReadAllText(jobsFile), JsonCodeGen.Default.InteractiveRebaseJobCollection); - var doneFile = Path.Combine(Path.GetDirectoryName(file), "rebase-merge", "done"); + var doneFile = Path.Combine(Path.GetDirectoryName(file)!, "rebase-merge", "done"); if (!File.Exists(doneFile)) return true; - var done = File.ReadAllText(doneFile).Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + var done = File.ReadAllText(doneFile).Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); if (done.Length > collection.Jobs.Count) return true; @@ -460,7 +480,7 @@ namespace SourceGit private bool TryLaunchedAsCoreEditor(IClassicDesktopStyleApplicationLifetime desktop) { var args = desktop.Args; - if (args.Length <= 1 || !args[0].Equals("--core-editor", StringComparison.Ordinal)) + if (args == null || args.Length <= 1 || !args[0].Equals("--core-editor", StringComparison.Ordinal)) return false; var file = args[1]; @@ -474,7 +494,7 @@ namespace SourceGit private bool TryLaunchedAsAskpass(IClassicDesktopStyleApplicationLifetime desktop) { var args = desktop.Args; - if (args.Length != 1 || !args[0].StartsWith("Enter passphrase", StringComparison.Ordinal)) + if (args == null || args.Length != 1 || !args[0].StartsWith("Enter passphrase", StringComparison.Ordinal)) return false; desktop.MainWindow = new Views.Askpass(args[0]); @@ -485,7 +505,10 @@ namespace SourceGit { Native.OS.SetupEnternalTools(); - var startupRepo = desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0]) ? desktop.Args[0] : null; + string startupRepo = null; + if (desktop.Args != null && desktop.Args.Length == 1 && Directory.Exists(desktop.Args[0])) + startupRepo = desktop.Args[0]; + _launcher = new ViewModels.Launcher(startupRepo); desktop.MainWindow = new Views.Launcher() { DataContext = _launcher }; diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs index 37b59962..48ef1825 100644 --- a/src/Commands/Command.cs +++ b/src/Commands/Command.cs @@ -19,7 +19,6 @@ namespace SourceGit.Commands { public bool IsSuccess { get; set; } public string StdOut { get; set; } - public string StdErr { get; set; } } public enum EditorType @@ -51,7 +50,7 @@ namespace SourceGit.Commands start.StandardErrorEncoding = Encoding.UTF8; // Force using this app as SSH askpass program - var selfExecFile = Process.GetCurrentProcess().MainModule.FileName; + var selfExecFile = Process.GetCurrentProcess().MainModule!.FileName; if (!OperatingSystem.IsLinux()) start.Environment.Add("DISPLAY", "required"); start.Environment.Add("SSH_ASKPASS", selfExecFile); // Can not use parameter here, because it invoked by SSH with `exec` @@ -199,20 +198,18 @@ namespace SourceGit.Commands { proc.Start(); } - catch (Exception e) + catch { return new ReadToEndResult() { IsSuccess = false, StdOut = string.Empty, - StdErr = e.Message, }; } var rs = new ReadToEndResult() { StdOut = proc.StandardOutput.ReadToEnd(), - StdErr = proc.StandardError.ReadToEnd(), }; proc.WaitForExit(); @@ -226,8 +223,5 @@ namespace SourceGit.Commands [GeneratedRegex(@"\d+%")] private static partial Regex REG_PROGRESS(); - - [GeneratedRegex(@"Enter\s+passphrase\s*for\s*key\s*['""]([^'""]+)['""]\:\s*", RegexOptions.IgnoreCase)] - private static partial Regex REG_ASKPASS(); } } diff --git a/src/Commands/Config.cs b/src/Commands/Config.cs index e1016f1e..67c17ce9 100644 --- a/src/Commands/Config.cs +++ b/src/Commands/Config.cs @@ -23,7 +23,7 @@ namespace SourceGit.Commands var rs = new Dictionary(); if (output.IsSuccess) { - var lines = output.StdOut.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + var lines = output.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { var idx = line.IndexOf('=', StringComparison.Ordinal); @@ -31,14 +31,7 @@ namespace SourceGit.Commands { var key = line.Substring(0, idx).Trim(); var val = line.Substring(idx + 1).Trim(); - if (rs.ContainsKey(key)) - { - rs[key] = val; - } - else - { - rs.Add(key, val); - } + rs[key] = val; } } } diff --git a/src/Commands/Fetch.cs b/src/Commands/Fetch.cs index 03bba91c..df5a1c2b 100644 --- a/src/Commands/Fetch.cs +++ b/src/Commands/Fetch.cs @@ -125,14 +125,7 @@ namespace SourceGit.Commands lock (_lock) { - if (_jobs.ContainsKey(repo)) - { - _jobs[repo] = job; - } - else - { - _jobs.Add(repo, job); - } + _jobs[repo] = job; } } diff --git a/src/Commands/GitFlow.cs b/src/Commands/GitFlow.cs index b115844d..5e05ed83 100644 --- a/src/Commands/GitFlow.cs +++ b/src/Commands/GitFlow.cs @@ -64,7 +64,7 @@ namespace SourceGit.Commands return init.Exec(); } - public static string Prefix(string repo, string type) + public static string GetPrefix(string repo, string type) { return new Config(repo).Get($"gitflow.prefix.{type}"); } diff --git a/src/Commands/LFS.cs b/src/Commands/LFS.cs index 62254d42..c9ab7b41 100644 --- a/src/Commands/LFS.cs +++ b/src/Commands/LFS.cs @@ -82,7 +82,7 @@ namespace SourceGit.Commands var rs = cmd.ReadToEnd(); if (rs.IsSuccess) { - var lines = rs.StdOut.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + var lines = rs.StdOut.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { var match = REG_LOCK().Match(line); diff --git a/src/Commands/QueryCommits.cs b/src/Commands/QueryCommits.cs index 401f1682..a42a8d75 100644 --- a/src/Commands/QueryCommits.cs +++ b/src/Commands/QueryCommits.cs @@ -53,8 +53,6 @@ namespace SourceGit.Commands _current.Subject = line; nextPartIdx = -1; break; - default: - break; } nextPartIdx++; @@ -97,6 +95,9 @@ namespace SourceGit.Commands foreach (var sub in subs) { var d = sub.Trim(); + if (d.EndsWith("/HEAD", StringComparison.Ordinal)) + continue; + if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal)) { _current.Decorators.Add(new Models.Decorator() @@ -105,10 +106,6 @@ namespace SourceGit.Commands Name = d.Substring(15), }); } - else if (d.EndsWith("/HEAD", StringComparison.Ordinal)) - { - continue; - } else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal)) { _current.IsMerged = true; @@ -159,10 +156,10 @@ namespace SourceGit.Commands private void MarkFirstMerged() { - Args = $"log --since=\"{_commits[_commits.Count - 1].CommitterTimeStr}\" --format=\"%H\""; + Args = $"log --since=\"{_commits[^1].CommitterTimeStr}\" --format=\"%H\""; var rs = ReadToEnd(); - var shas = rs.StdOut.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); + var shas = rs.StdOut.Split('\n', StringSplitOptions.RemoveEmptyEntries); if (shas.Length == 0) return; diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index 2f1e89b8..a9f777b1 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -52,7 +52,7 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.None, Models.ChangeState.Copied); break; case "M": - change.Set(Models.ChangeState.Modified, Models.ChangeState.None); + change.Set(Models.ChangeState.Modified); break; case "MM": change.Set(Models.ChangeState.Modified, Models.ChangeState.Modified); @@ -61,7 +61,7 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.Modified, Models.ChangeState.Deleted); break; case "A": - change.Set(Models.ChangeState.Added, Models.ChangeState.None); + change.Set(Models.ChangeState.Added); break; case "AM": change.Set(Models.ChangeState.Added, Models.ChangeState.Modified); @@ -70,10 +70,10 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.Added, Models.ChangeState.Deleted); break; case "D": - change.Set(Models.ChangeState.Deleted, Models.ChangeState.None); + change.Set(Models.ChangeState.Deleted); break; case "R": - change.Set(Models.ChangeState.Renamed, Models.ChangeState.None); + change.Set(Models.ChangeState.Renamed); break; case "RM": change.Set(Models.ChangeState.Renamed, Models.ChangeState.Modified); @@ -82,7 +82,7 @@ namespace SourceGit.Commands change.Set(Models.ChangeState.Renamed, Models.ChangeState.Deleted); break; case "C": - change.Set(Models.ChangeState.Copied, Models.ChangeState.None); + change.Set(Models.ChangeState.Copied); break; case "CM": change.Set(Models.ChangeState.Copied, Models.ChangeState.Modified); diff --git a/src/Commands/QuerySingleCommit.cs b/src/Commands/QuerySingleCommit.cs index a3b13929..f65b6ce1 100644 --- a/src/Commands/QuerySingleCommit.cs +++ b/src/Commands/QuerySingleCommit.cs @@ -47,6 +47,9 @@ namespace SourceGit.Commands foreach (var sub in subs) { var d = sub.Trim(); + if (d.EndsWith("/HEAD", StringComparison.Ordinal)) + continue; + if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal)) { decorators.Add(new Models.Decorator() @@ -55,10 +58,6 @@ namespace SourceGit.Commands Name = d.Substring(15), }); } - else if (d.EndsWith("/HEAD", StringComparison.Ordinal)) - { - continue; - } else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal)) { isHeadOfCurrent = true; diff --git a/src/Commands/QueryTags.cs b/src/Commands/QueryTags.cs index 0b5b747f..f17b7896 100644 --- a/src/Commands/QueryTags.cs +++ b/src/Commands/QueryTags.cs @@ -20,7 +20,7 @@ namespace SourceGit.Commands protected override void OnReadline(string line) { - var subs = line.Split(new char[] { '$' }, StringSplitOptions.RemoveEmptyEntries); + var subs = line.Split('$', StringSplitOptions.RemoveEmptyEntries); if (subs.Length == 2) { _loaded.Add(new Models.Tag() diff --git a/src/Commands/Statistics.cs b/src/Commands/Statistics.cs index 85f5a4fb..b9f33367 100644 --- a/src/Commands/Statistics.cs +++ b/src/Commands/Statistics.cs @@ -27,11 +27,8 @@ namespace SourceGit.Commands return; var dateStr = line.Substring(0, dateEndIdx); - var date = 0.0; - if (!double.TryParse(dateStr, out date)) - return; - - _statistics.AddCommit(line.Substring(dateEndIdx + 1), date); + if (double.TryParse(dateStr, out var date)) + _statistics.AddCommit(line.Substring(dateEndIdx + 1), date); } private readonly Models.Statistics _statistics = null; diff --git a/src/Commands/Worktree.cs b/src/Commands/Worktree.cs index a0c57df7..7516b1e3 100644 --- a/src/Commands/Worktree.cs +++ b/src/Commands/Worktree.cs @@ -1,14 +1,10 @@ using System; using System.Collections.Generic; -using System.Text.RegularExpressions; namespace SourceGit.Commands { - public partial class Worktree : Command + public class Worktree : Command { - [GeneratedRegex(@"^(\w)\s(\d+)$")] - private static partial Regex REG_AHEAD_BEHIND(); - public Worktree(string repo) { WorkingDirectory = repo; @@ -24,7 +20,7 @@ namespace SourceGit.Commands var last = null as Models.Worktree; if (rs.IsSuccess) { - var lines = rs.StdOut.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + var lines = rs.StdOut.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { if (line.StartsWith("worktree ", StringComparison.Ordinal)) @@ -34,27 +30,23 @@ namespace SourceGit.Commands } else if (line.StartsWith("bare", StringComparison.Ordinal)) { - last.IsBare = true; + last!.IsBare = true; } else if (line.StartsWith("HEAD ", StringComparison.Ordinal)) { - last.Head = line.Substring(5).Trim(); + last!.Head = line.Substring(5).Trim(); } else if (line.StartsWith("branch ", StringComparison.Ordinal)) { - last.Branch = line.Substring(7).Trim(); + last!.Branch = line.Substring(7).Trim(); } else if (line.StartsWith("detached", StringComparison.Ordinal)) { - last.IsDetached = true; + last!.IsDetached = true; } else if (line.StartsWith("locked", StringComparison.Ordinal)) { - last.IsLocked = true; - } - else if (line.StartsWith("prunable", StringComparison.Ordinal)) - { - last.IsPrunable = true; + last!.IsLocked = true; } } } diff --git a/src/Converters/ChangeViewModeConverters.cs b/src/Converters/ChangeViewModeConverters.cs index a5b07bca..59a5652c 100644 --- a/src/Converters/ChangeViewModeConverters.cs +++ b/src/Converters/ChangeViewModeConverters.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls; +using Avalonia; +using Avalonia.Controls; using Avalonia.Data.Converters; using Avalonia.Media; @@ -12,11 +13,11 @@ namespace SourceGit.Converters switch (v) { case Models.ChangeViewMode.List: - return App.Current?.FindResource("Icons.List") as StreamGeometry; + return Application.Current?.FindResource("Icons.List") as StreamGeometry; case Models.ChangeViewMode.Grid: - return App.Current?.FindResource("Icons.Grid") as StreamGeometry; + return Application.Current?.FindResource("Icons.Grid") as StreamGeometry; default: - return App.Current?.FindResource("Icons.Tree") as StreamGeometry; + return Application.Current?.FindResource("Icons.Tree") as StreamGeometry; } }); } diff --git a/src/Converters/StringConverters.cs b/src/Converters/StringConverters.cs index 42eaa164..a3e3bbba 100644 --- a/src/Converters/StringConverters.cs +++ b/src/Converters/StringConverters.cs @@ -18,7 +18,7 @@ namespace SourceGit.Converters public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - return (value as Models.Locale).Key; + return (value as Models.Locale)?.Key; } } @@ -29,18 +29,21 @@ namespace SourceGit.Converters public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var theme = (string)value; + if (string.IsNullOrEmpty(theme)) + return ThemeVariant.Default; + if (theme.Equals("Light", StringComparison.OrdinalIgnoreCase)) return ThemeVariant.Light; - else if (theme.Equals("Dark", StringComparison.OrdinalIgnoreCase)) + + if (theme.Equals("Dark", StringComparison.OrdinalIgnoreCase)) return ThemeVariant.Dark; - else - return ThemeVariant.Default; + + return ThemeVariant.Default; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - var theme = (ThemeVariant)value; - return theme.Key; + return (value as ThemeVariant)?.Key; } } diff --git a/src/Models/AvatarManager.cs b/src/Models/AvatarManager.cs index 50d890e1..ed39bcf7 100644 --- a/src/Models/AvatarManager.cs +++ b/src/Models/AvatarManager.cs @@ -75,7 +75,10 @@ namespace SourceGit.Models } } } - catch { } + catch + { + // ignored + } lock (_synclock) { @@ -84,10 +87,7 @@ namespace SourceGit.Models Dispatcher.UIThread.InvokeAsync(() => { - if (_resources.ContainsKey(md5)) - _resources[md5] = img; - else - _resources.Add(md5, img); + _resources[md5] = img; NotifyResourceChanged(md5); }); } @@ -134,7 +134,10 @@ namespace SourceGit.Models return img; } } - catch { } + catch + { + // ignore + } } } @@ -156,7 +159,7 @@ namespace SourceGit.Models } private static readonly object _synclock = new object(); - private static readonly string _storePath = string.Empty; + private static readonly string _storePath; private static readonly List _avatars = new List(); private static readonly Dictionary _resources = new Dictionary(); private static readonly HashSet _requesting = new HashSet(); diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index 10ccebfb..30c2c499 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -22,9 +22,8 @@ namespace SourceGit.Models public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss"); public string CommitterTimeStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss"); public string AuthorTimeShortStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd"); - public string CommitterTimeShortStr => DateTime.UnixEpoch.AddSeconds(CommitterTime).ToString("yyyy/MM/dd"); - public bool IsCommitterVisible => Author != Committer || AuthorTime != CommitterTime; + public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime; public bool IsCurrentHead => Decorators.Find(x => x.Type is DecoratorType.CurrentBranchHead or DecoratorType.CurrentCommitHead) != null; } } diff --git a/src/Models/CommitGraph.cs b/src/Models/CommitGraph.cs index 4e9bbc4d..6f371594 100644 --- a/src/Models/CommitGraph.cs +++ b/src/Models/CommitGraph.cs @@ -268,8 +268,9 @@ namespace SourceGit.Models var path = unsolved[i]; var endY = (commits.Count - 0.5) * UNIT_HEIGHT; - if (path.Path.Points.Count == 1 && path.Path.Points[0].Y == endY) + if (path.Path.Points.Count == 1 && Math.Abs(path.Path.Points[0].Y - endY) < 0.0001) continue; + path.Add((i + 0.5) * UNIT_WIDTH, endY + HALF_HEIGHT, HALF_HEIGHT, true); } unsolved.Clear(); diff --git a/src/Models/DiffOption.cs b/src/Models/DiffOption.cs index e98edffb..f5bec602 100644 --- a/src/Models/DiffOption.cs +++ b/src/Models/DiffOption.cs @@ -108,8 +108,8 @@ namespace SourceGit.Models private readonly Change _workingCopyChange = null; private readonly bool _isUnstaged = false; + private readonly string _path; private readonly string _orgPath = string.Empty; - private readonly string _path = string.Empty; private readonly string _extra = string.Empty; private readonly List _revisions = new List(); } diff --git a/src/Models/ExternalTool.cs b/src/Models/ExternalTool.cs index 98351cd6..dc164f59 100644 --- a/src/Models/ExternalTool.cs +++ b/src/Models/ExternalTool.cs @@ -12,9 +12,9 @@ namespace SourceGit.Models { public class ExternalTool { - public string Name { get; private set; } = string.Empty; - public string Executable { get; private set; } = string.Empty; - public string OpenCmdArgs { get; private set; } = string.Empty; + public string Name { get; private set; } + public string Executable { get; private set; } + public string OpenCmdArgs { get; private set; } public Bitmap IconImage { get; private set; } = null; public ExternalTool(string name, string icon, string executable, string openCmdArgs) @@ -25,10 +25,14 @@ namespace SourceGit.Models try { - var asset = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{icon}.png", UriKind.RelativeOrAbsolute)); + var asset = AssetLoader.Open(new Uri($"avares://SourceGit/Resources/ExternalToolIcons/{icon}.png", + UriKind.RelativeOrAbsolute)); IconImage = new Bitmap(asset); } - catch { } + catch + { + // ignore + } } public void Open(string repo) diff --git a/src/Models/RevisionFile.cs b/src/Models/RevisionFile.cs index 59868fcc..e2f4abb0 100644 --- a/src/Models/RevisionFile.cs +++ b/src/Models/RevisionFile.cs @@ -14,7 +14,6 @@ namespace SourceGit.Models public class RevisionTextFile { - public string FileName { get; set; } public string Content { get; set; } } diff --git a/src/Models/TextMateHelper.cs b/src/Models/TextMateHelper.cs index 044f2b29..0ae46c90 100644 --- a/src/Models/TextMateHelper.cs +++ b/src/Models/TextMateHelper.cs @@ -1,6 +1,7 @@ using System; using System.IO; +using Avalonia; using Avalonia.Styling; using AvaloniaEdit; @@ -14,14 +15,10 @@ namespace SourceGit.Models { public static TextMate.Installation CreateForEditor(TextEditor editor) { - if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) - { + if (Application.Current?.ActualThemeVariant == ThemeVariant.Dark) return editor.InstallTextMate(new RegistryOptions(ThemeName.DarkPlus)); - } - else - { - return editor.InstallTextMate(new RegistryOptions(ThemeName.LightPlus)); - } + + return editor.InstallTextMate(new RegistryOptions(ThemeName.LightPlus)); } public static void SetThemeByApp(TextMate.Installation installation) @@ -29,35 +26,28 @@ namespace SourceGit.Models if (installation == null) return; - var reg = installation.RegistryOptions as RegistryOptions; - if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) + if (installation.RegistryOptions is RegistryOptions reg) { - installation.SetTheme(reg.LoadTheme(ThemeName.DarkPlus)); - } - else - { - installation.SetTheme(reg.LoadTheme(ThemeName.LightPlus)); + if (Application.Current?.ActualThemeVariant == ThemeVariant.Dark) + installation.SetTheme(reg.LoadTheme(ThemeName.DarkPlus)); + else + installation.SetTheme(reg.LoadTheme(ThemeName.LightPlus)); } } public static void SetGrammarByFileName(TextMate.Installation installation, string filePath) { - if (installation == null) - return; - - var ext = Path.GetExtension(filePath); - if (ext == ".h") + if (installation is { RegistryOptions: RegistryOptions reg }) { - ext = ".cpp"; - } - else if (ext == ".resx" || ext == ".plist") - { - ext = ".xml"; - } + var ext = Path.GetExtension(filePath); + if (ext == ".h") + ext = ".cpp"; + else if (ext == ".resx" || ext == ".plist") + ext = ".xml"; - var reg = installation.RegistryOptions as RegistryOptions; - installation.SetGrammar(reg.GetScopeByExtension(ext)); - GC.Collect(); + installation.SetGrammar(reg.GetScopeByExtension(ext)); + GC.Collect(); + } } } } diff --git a/src/Models/User.cs b/src/Models/User.cs index cb0d21cd..850bcf2f 100644 --- a/src/Models/User.cs +++ b/src/Models/User.cs @@ -10,6 +10,20 @@ namespace SourceGit.Models public string Name { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; + public User() + { + // Only used by User.Invalid + } + + public User(string data) + { + var nameEndIdx = data.IndexOf('±', StringComparison.Ordinal); + + Name = nameEndIdx > 0 ? data.Substring(0, nameEndIdx) : string.Empty; + Email = data.Substring(nameEndIdx + 1); + _hash = data.GetHashCode(); + } + public override bool Equals(object obj) { if (obj == null || !(obj is User)) @@ -21,21 +35,15 @@ namespace SourceGit.Models public override int GetHashCode() { - return base.GetHashCode(); + return _hash; } public static User FindOrAdd(string data) { - return _caches.GetOrAdd(data, key => - { - var nameEndIdx = key.IndexOf('±', StringComparison.Ordinal); - var name = nameEndIdx > 0 ? key.Substring(0, nameEndIdx) : string.Empty; - var email = key.Substring(nameEndIdx + 1); - - return new User() { Name = name, Email = email }; - }); + return _caches.GetOrAdd(data, key => new User(key)); } private static ConcurrentDictionary _caches = new ConcurrentDictionary(); + private readonly int _hash; } } diff --git a/src/Models/Version.cs b/src/Models/Version.cs index c16c1233..aca1f0fc 100644 --- a/src/Models/Version.cs +++ b/src/Models/Version.cs @@ -28,7 +28,7 @@ namespace SourceGit.Models var major = int.Parse(match.Groups[1].Value); var minor = int.Parse(match.Groups[2].Value); - var ver = Assembly.GetExecutingAssembly().GetName().Version; + var ver = Assembly.GetExecutingAssembly().GetName().Version!; return ver.Major < major || (ver.Major == major && ver.Minor < minor); } } diff --git a/src/Models/Watcher.cs b/src/Models/Watcher.cs index de3a42c4..2a1c4cc6 100644 --- a/src/Models/Watcher.cs +++ b/src/Models/Watcher.cs @@ -38,7 +38,7 @@ namespace SourceGit.Models // If this repository is a worktree repository, just watch the main repository's gitdir. var gitDirNormalized = _repo.GitDir.Replace("\\", "/"); - var worktreeIdx = gitDirNormalized.IndexOf(".git/worktrees/"); + var worktreeIdx = gitDirNormalized.IndexOf(".git/worktrees/", StringComparison.Ordinal); var repoWatchDir = _repo.GitDir; if (worktreeIdx > 0) repoWatchDir = _repo.GitDir.Substring(0, worktreeIdx + 4); diff --git a/src/Models/Worktree.cs b/src/Models/Worktree.cs index 11525387..f9ba14e4 100644 --- a/src/Models/Worktree.cs +++ b/src/Models/Worktree.cs @@ -9,7 +9,6 @@ namespace SourceGit.Models public string Head { get; set; } = string.Empty; public bool IsBare { get; set; } = false; public bool IsDetached { get; set; } = false; - public bool IsPrunable { get; set; } = false; public bool IsLocked { diff --git a/src/Native/Linux.cs b/src/Native/Linux.cs index bca2f2ab..dbcd43aa 100644 --- a/src/Native/Linux.cs +++ b/src/Native/Linux.cs @@ -15,8 +15,8 @@ namespace SourceGit.Native { class Terminal { - public string FilePath { get; set; } = string.Empty; - public string OpenArgFormat { get; set; } = string.Empty; + public string FilePath { get; set; } + public string OpenArgFormat { get; set; } public Terminal(string exec, string fmt) { @@ -115,12 +115,15 @@ namespace SourceGit.Native } var proc = Process.Start(_xdgOpenPath, $"\"{file}\""); - proc.WaitForExit(); + if (proc != null) + { + proc.WaitForExit(); - if (proc.ExitCode != 0) - App.RaiseException("", $"Failed to open \"{file}\""); + if (proc.ExitCode != 0) + App.RaiseException("", $"Failed to open \"{file}\""); - proc.Close(); + proc.Close(); + } } private string FindExecutable(string filename) @@ -177,7 +180,7 @@ namespace SourceGit.Native return File.Exists(path) ? path : FindExecutable("fleet"); } - private string _xdgOpenPath = string.Empty; + private string _xdgOpenPath = null; private Terminal _terminal = null; } } diff --git a/src/Native/MacOS.cs b/src/Native/MacOS.cs index d5b58fb3..4fc9998c 100644 --- a/src/Native/MacOS.cs +++ b/src/Native/MacOS.cs @@ -78,7 +78,10 @@ namespace SourceGit.Native File.WriteAllText(tmp, builder.ToString()); var proc = Process.Start("osascript", $"\"{tmp}\""); - proc.Exited += (o, e) => File.Delete(tmp); + if (proc != null) + proc.Exited += (_, _) => File.Delete(tmp); + else + File.Delete(tmp); } public void OpenWithDefaultEditor(string file) diff --git a/src/Native/OS.cs b/src/Native/OS.cs index 451362ca..8c2a3ada 100644 --- a/src/Native/OS.cs +++ b/src/Native/OS.cs @@ -48,20 +48,16 @@ namespace SourceGit.Native public static Models.Shell GetShell() { if (OperatingSystem.IsWindows()) - { - return (_backend as Windows).Shell; - } - else - { - return Models.Shell.Default; - } + return (_backend as Windows)!.Shell; + + return Models.Shell.Default; } public static bool SetShell(Models.Shell shell) { if (OperatingSystem.IsWindows()) { - var windows = _backend as Windows; + var windows = (_backend as Windows)!; if (windows.Shell != shell) { windows.Shell = shell; diff --git a/src/Native/Windows.cs b/src/Native/Windows.cs index bdf4d74c..d8203d13 100644 --- a/src/Native/Windows.cs +++ b/src/Native/Windows.cs @@ -73,8 +73,8 @@ namespace SourceGit.Native v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(); if (RtlGetVersion(ref v) == 0 && (v.dwMajorVersion < 10 || v.dwBuildNumber < 22000)) { - Window.WindowStateProperty.Changed.AddClassHandler((w, e) => ExtendWindowFrame(w)); - Window.LoadedEvent.AddClassHandler((w, e) => ExtendWindowFrame(w)); + Window.WindowStateProperty.Changed.AddClassHandler((w, _) => ExtendWindowFrame(w)); + Control.LoadedEvent.AddClassHandler((w, _) => ExtendWindowFrame(w)); } } @@ -85,9 +85,9 @@ namespace SourceGit.Native Microsoft.Win32.RegistryView.Registry64); var git = reg.OpenSubKey("SOFTWARE\\GitForWindows"); - if (git != null) + if (git != null && git.GetValue("InstallPath") is string installPath) { - return Path.Combine(git.GetValue("InstallPath") as string, "bin", "git.exe"); + return Path.Combine(installPath, "bin", "git.exe"); } var builder = new StringBuilder("git.exe", 259); @@ -137,7 +137,13 @@ namespace SourceGit.Native switch (Shell) { case Models.Shell.Default: - var binDir = Path.GetDirectoryName(OS.GitExecutable); + if (string.IsNullOrEmpty(OS.GitExecutable)) + { + App.RaiseException(workdir, $"Can NOT found bash.exe"); + return; + } + + var binDir = Path.GetDirectoryName(OS.GitExecutable)!; var bash = Path.Combine(binDir, "bash.exe"); if (!File.Exists(bash)) { @@ -175,28 +181,23 @@ namespace SourceGit.Native public void OpenInFileManager(string path, bool select) { - var fullpath = string.Empty; + string fullpath; if (File.Exists(path)) { fullpath = new FileInfo(path).FullName; - - // For security reason, we never execute a file. - // Instead, we open the folder and select it. select = true; } else { - fullpath = new DirectoryInfo(path).FullName; + fullpath = new DirectoryInfo(path!).FullName; } if (select) { - // The fullpath here may be a file or a folder. OpenFolderAndSelectFile(fullpath); } else { - // The fullpath here is always a folder. Process.Start(new ProcessStartInfo(fullpath) { UseShellExecute = true, @@ -362,7 +363,7 @@ namespace SourceGit.Native if (sublime != null) { var icon = sublime.GetValue("DisplayIcon") as string; - return Path.Combine(Path.GetDirectoryName(icon), "subl.exe"); + return Path.Combine(Path.GetDirectoryName(icon)!, "subl.exe"); } // Sublime Text 3 @@ -370,7 +371,7 @@ namespace SourceGit.Native if (sublime3 != null) { var icon = sublime3.GetValue("DisplayIcon") as string; - return Path.Combine(Path.GetDirectoryName(icon), "subl.exe"); + return Path.Combine(Path.GetDirectoryName(icon)!, "subl.exe"); } return string.Empty; diff --git a/src/ViewModels/AddWorktree.cs b/src/ViewModels/AddWorktree.cs index 00cf604b..cf736029 100644 --- a/src/ViewModels/AddWorktree.cs +++ b/src/ViewModels/AddWorktree.cs @@ -88,6 +88,9 @@ namespace SourceGit.ViewModels if (creator == null) return new ValidationResult("Missing runtime context to create branch!"); + if (string.IsNullOrEmpty(path)) + return new ValidationResult("Worktree path is required!"); + var fullPath = System.IO.Path.IsPathRooted(path) ? path : System.IO.Path.Combine(creator._repo.FullPath, path); var info = new DirectoryInfo(fullPath); if (info.Exists) diff --git a/src/ViewModels/Archive.cs b/src/ViewModels/Archive.cs index 3bc3b6be..366a6179 100644 --- a/src/ViewModels/Archive.cs +++ b/src/ViewModels/Archive.cs @@ -6,7 +6,6 @@ namespace SourceGit.ViewModels { public class Archive : Popup { - [Required(ErrorMessage = "Output file name is required")] public string SaveFile { @@ -67,7 +66,7 @@ namespace SourceGit.ViewModels } private readonly Repository _repo = null; - private string _saveFile = string.Empty; - private readonly string _revision = string.Empty; + private string _saveFile; + private readonly string _revision; } } diff --git a/src/ViewModels/Blame.cs b/src/ViewModels/Blame.cs index 79770c6f..26d77b23 100644 --- a/src/ViewModels/Blame.cs +++ b/src/ViewModels/Blame.cs @@ -14,12 +14,6 @@ namespace SourceGit.ViewModels private set; } - public string SelectedSHA - { - get => _selectedSHA; - private set => SetProperty(ref _selectedSHA, value); - } - public bool IsBinary { get => _data != null && _data.IsBinary; @@ -53,8 +47,7 @@ namespace SourceGit.ViewModels repo?.NavigateToCommit(commitSHA); } - private readonly string _repo = string.Empty; - private string _selectedSHA = string.Empty; + private readonly string _repo; private Models.BlameData _data = null; } } diff --git a/src/ViewModels/BranchCompare.cs b/src/ViewModels/BranchCompare.cs index 634d18a6..337162b9 100644 --- a/src/ViewModels/BranchCompare.cs +++ b/src/ViewModels/BranchCompare.cs @@ -201,7 +201,7 @@ namespace SourceGit.ViewModels } } - private string _repo = string.Empty; + private string _repo; private Models.Commit _baseHead = null; private Models.Commit _toHead = null; private List _changes = null; diff --git a/src/ViewModels/ChangeCollection.cs b/src/ViewModels/ChangeCollection.cs new file mode 100644 index 00000000..76fca0e6 --- /dev/null +++ b/src/ViewModels/ChangeCollection.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +using Avalonia.Collections; + +namespace SourceGit.ViewModels +{ + public class ChangeCollectionAsTree + { + public List Tree { get; set; } = new List(); + public AvaloniaList Rows { get; set; } = new AvaloniaList(); + } + + public class ChangeCollectionAsGrid + { + public AvaloniaList Changes { get; set; } = new AvaloniaList(); + } + + public class ChangeCollectionAsList + { + public AvaloniaList Changes { get; set; } = new AvaloniaList(); + } +} diff --git a/src/ViewModels/ChangeTreeNode.cs b/src/ViewModels/ChangeTreeNode.cs index 5598de5d..49494b2b 100644 --- a/src/ViewModels/ChangeTreeNode.cs +++ b/src/ViewModels/ChangeTreeNode.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; -using Avalonia.Collections; - using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { public class ChangeTreeNode : ObservableObject { - public string FullPath { get; set; } = string.Empty; + public string FullPath { get; set; } public int Depth { get; private set; } = 0; public Models.Change Change { get; set; } = null; public List Children { get; set; } = new List(); @@ -55,8 +53,7 @@ namespace SourceGit.ViewModels else { ChangeTreeNode lastFolder = null; - var start = 0; - var depth = 0; + int depth = 0; while (sepIdx != -1) { @@ -79,12 +76,11 @@ namespace SourceGit.ViewModels lastFolder = cur; } - start = sepIdx + 1; depth++; - sepIdx = c.Path.IndexOf('/', start); + sepIdx = c.Path.IndexOf('/', sepIdx + 1); } - lastFolder.Children.Add(new ChangeTreeNode(c, depth)); + lastFolder?.Children.Add(new ChangeTreeNode(c, depth)); } } @@ -126,20 +122,4 @@ namespace SourceGit.ViewModels private bool _isExpanded = true; } - - public class ChangeCollectionAsTree - { - public List Tree { get; set; } = new List(); - public AvaloniaList Rows { get; set; } = new AvaloniaList(); - } - - public class ChangeCollectionAsGrid - { - public AvaloniaList Changes { get; set; } = new AvaloniaList(); - } - - public class ChangeCollectionAsList - { - public AvaloniaList Changes { get; set; } = new AvaloniaList(); - } } diff --git a/src/ViewModels/Clone.cs b/src/ViewModels/Clone.cs index bd74b754..819ef83d 100644 --- a/src/ViewModels/Clone.cs +++ b/src/ViewModels/Clone.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; @@ -105,8 +106,8 @@ namespace SourceGit.ViewModels } else { - var name = Path.GetFileName(_remote); - if (name.EndsWith(".git")) + var name = Path.GetFileName(_remote)!; + if (name.EndsWith(".git", StringComparison.Ordinal)) name = name.Substring(0, name.Length - 4); path = Path.GetFullPath(Path.Combine(path, name)); } diff --git a/src/ViewModels/CommitDetail.cs b/src/ViewModels/CommitDetail.cs index c401f20a..7eea56a3 100644 --- a/src/ViewModels/CommitDetail.cs +++ b/src/ViewModels/CommitDetail.cs @@ -176,14 +176,8 @@ namespace SourceGit.ViewModels } else { - Dispatcher.UIThread.Invoke(() => - { - ViewRevisionFileContent = new Models.RevisionTextFile() - { - FileName = file.Path, - Content = content - }; - }); + var txt = new Models.RevisionTextFile() { Content = content }; + Dispatcher.UIThread.Invoke(() => ViewRevisionFileContent = txt); } }); break; @@ -256,7 +250,7 @@ namespace SourceGit.ViewModels var blame = new MenuItem(); blame.Header = App.Text("Blame"); blame.Icon = App.CreateMenuIcon("Icons.Blame"); - blame.Click += (o, ev) => + blame.Click += (_, ev) => { var window = new Views.Blame() { DataContext = new Blame(_repo, change.Path, _commit.SHA) }; window.Show(); @@ -320,7 +314,7 @@ namespace SourceGit.ViewModels blame.Header = App.Text("Blame"); blame.Icon = App.CreateMenuIcon("Icons.Blame"); blame.IsEnabled = file.Type == Models.ObjectType.Blob; - blame.Click += (o, ev) => + blame.Click += (_, ev) => { var window = new Views.Blame() { DataContext = new Blame(_repo, file.Path, _commit.SHA) }; window.Show(); @@ -461,7 +455,7 @@ namespace SourceGit.ViewModels ".ico", ".bmp", ".jpg", ".png", ".jpeg" }; - private string _repo = string.Empty; + private string _repo; private int _activePageIndex = 0; private Models.Commit _commit = null; private string _fullMessage = string.Empty; diff --git a/src/ViewModels/CreateTag.cs b/src/ViewModels/CreateTag.cs index 7f1ac161..0a7d7440 100644 --- a/src/ViewModels/CreateTag.cs +++ b/src/ViewModels/CreateTag.cs @@ -81,7 +81,7 @@ namespace SourceGit.ViewModels return Task.Run(() => { - var succ = false; + bool succ; if (_annotated) succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn, Message, SignTag); else @@ -104,6 +104,6 @@ namespace SourceGit.ViewModels private readonly Repository _repo = null; private string _tagName = string.Empty; private bool _annotated = true; - private readonly string _basedOn = string.Empty; + private readonly string _basedOn; } } diff --git a/src/ViewModels/DiffContext.cs b/src/ViewModels/DiffContext.cs index c3531ffc..a2b435cc 100644 --- a/src/ViewModels/DiffContext.cs +++ b/src/ViewModels/DiffContext.cs @@ -30,7 +30,6 @@ namespace SourceGit.ViewModels public string Title { get => _title; - private set => SetProperty(ref _title, value); } public string FileModeChange @@ -237,9 +236,9 @@ namespace SourceGit.ViewModels ".ico", ".bmp", ".jpg", ".png", ".jpeg" }; - private readonly string _repo = string.Empty; + private readonly string _repo; private readonly Models.DiffOption _option = null; - private string _title = string.Empty; + private string _title; private string _fileModeChange = string.Empty; private bool _isLoading = true; private bool _isTextDiff = false; diff --git a/src/ViewModels/Discard.cs b/src/ViewModels/Discard.cs index 368577ff..783d3c08 100644 --- a/src/ViewModels/Discard.cs +++ b/src/ViewModels/Discard.cs @@ -9,7 +9,6 @@ namespace SourceGit.ViewModels public class Discard : Popup { - public object Mode { get; diff --git a/src/ViewModels/EditRemote.cs b/src/ViewModels/EditRemote.cs index 5fd22d26..0004cca4 100644 --- a/src/ViewModels/EditRemote.cs +++ b/src/ViewModels/EditRemote.cs @@ -67,7 +67,7 @@ namespace SourceGit.ViewModels foreach (var remote in edit._repo.Remotes) { if (remote != edit._remote && name == remote.Name) - new ValidationResult("A remote with given name already exists!!!"); + return new ValidationResult("A remote with given name already exists!!!"); } } @@ -84,7 +84,7 @@ namespace SourceGit.ViewModels foreach (var remote in edit._repo.Remotes) { if (remote != edit._remote && url == remote.URL) - new ValidationResult("A remote with the same url already exists!!!"); + return new ValidationResult("A remote with the same url already exists!!!"); } } @@ -136,8 +136,8 @@ namespace SourceGit.ViewModels private readonly Repository _repo = null; private readonly Models.Remote _remote = null; - private string _name = string.Empty; - private string _url = string.Empty; + private string _name = null; + private string _url = null; private bool _useSSH = false; private string _sshkey = string.Empty; } diff --git a/src/ViewModels/EditRepositoryNode.cs b/src/ViewModels/EditRepositoryNode.cs index 4877ca9b..a4172d92 100644 --- a/src/ViewModels/EditRepositoryNode.cs +++ b/src/ViewModels/EditRepositoryNode.cs @@ -5,12 +5,6 @@ namespace SourceGit.ViewModels { public class EditRepositoryNode : Popup { - public RepositoryNode Node - { - get => _node; - set => SetProperty(ref _node, value); - } - public string Id { get => _id; @@ -60,8 +54,8 @@ namespace SourceGit.ViewModels } private RepositoryNode _node = null; - private string _id = string.Empty; - private string _name = string.Empty; + private string _id = null; + private string _name = null; private bool _isRepository = false; private int _bookmark = 0; } diff --git a/src/ViewModels/Fetch.cs b/src/ViewModels/Fetch.cs index 437bef70..fa1986b3 100644 --- a/src/ViewModels/Fetch.cs +++ b/src/ViewModels/Fetch.cs @@ -68,6 +68,6 @@ namespace SourceGit.ViewModels } private readonly Repository _repo = null; - private bool _fetchAllRemotes = true; + private bool _fetchAllRemotes; } } diff --git a/src/ViewModels/FileHistories.cs b/src/ViewModels/FileHistories.cs index 5256da49..f938326e 100644 --- a/src/ViewModels/FileHistories.cs +++ b/src/ViewModels/FileHistories.cs @@ -73,8 +73,8 @@ namespace SourceGit.ViewModels }); } - private readonly string _repo = string.Empty; - private readonly string _file = string.Empty; + private readonly string _repo = null; + private readonly string _file = null; private bool _isLoading = true; private List _commits = null; private Models.Commit _selectedCommit = null; diff --git a/src/ViewModels/GitFlowFinish.cs b/src/ViewModels/GitFlowFinish.cs index 7db69366..d278dc77 100644 --- a/src/ViewModels/GitFlowFinish.cs +++ b/src/ViewModels/GitFlowFinish.cs @@ -43,8 +43,8 @@ namespace SourceGit.ViewModels }); } - private readonly Repository _repo = null; - private readonly string _type = "feature"; - private readonly string _prefix = string.Empty; + private readonly Repository _repo; + private readonly string _type; + private readonly string _prefix; } } diff --git a/src/ViewModels/GitFlowStart.cs b/src/ViewModels/GitFlowStart.cs index ca580d99..af1b8875 100644 --- a/src/ViewModels/GitFlowStart.cs +++ b/src/ViewModels/GitFlowStart.cs @@ -27,7 +27,7 @@ namespace SourceGit.ViewModels { _repo = repo; _type = type; - _prefix = Commands.GitFlow.Prefix(repo.FullPath, type); + _prefix = Commands.GitFlow.GetPrefix(repo.FullPath, type); View = new Views.GitFlowStart() { DataContext = this }; } @@ -59,9 +59,9 @@ namespace SourceGit.ViewModels }); } - private readonly Repository _repo = null; - private readonly string _type = "feature"; - private readonly string _prefix = string.Empty; + private readonly Repository _repo; + private readonly string _type; + private readonly string _prefix; private string _name = null; } } diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 5d035f3b..c5150a38 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Platform.Storage; - +using Avalonia.VisualTree; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -156,7 +156,7 @@ namespace SourceGit.ViewModels if (current == null) return null; - var commit = datagrid.SelectedItem as Models.Commit; + var commit = (datagrid.SelectedItem as Models.Commit)!; var menu = new ContextMenu(); var tags = new List(); @@ -202,7 +202,7 @@ namespace SourceGit.ViewModels var reset = new MenuItem(); reset.Header = new Views.NameHighlightedTextBlock("CommitCM.Reset", current.Name); reset.Icon = App.CreateMenuIcon("Icons.Reset"); - reset.Click += (o, e) => + reset.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Reset(_repo, current, commit)); @@ -215,7 +215,7 @@ namespace SourceGit.ViewModels var reword = new MenuItem(); reword.Header = App.Text("CommitCM.Reword"); reword.Icon = App.CreateMenuIcon("Icons.Edit"); - reword.Click += (o, e) => + reword.Click += (_, e) => { if (_repo.WorkingCopyChangesCount > 0) { @@ -233,7 +233,7 @@ namespace SourceGit.ViewModels squash.Header = App.Text("CommitCM.Squash"); squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent"); squash.IsEnabled = commit.Parents.Count == 1; - squash.Click += (o, e) => + squash.Click += (_, e) => { if (_repo.WorkingCopyChangesCount > 0) { @@ -258,7 +258,7 @@ namespace SourceGit.ViewModels var rebase = new MenuItem(); rebase.Header = new Views.NameHighlightedTextBlock("CommitCM.Rebase", current.Name); rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); - rebase.Click += (o, e) => + rebase.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(_repo, current, commit)); @@ -269,7 +269,7 @@ namespace SourceGit.ViewModels var cherryPick = new MenuItem(); cherryPick.Header = App.Text("CommitCM.CherryPick"); cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick"); - cherryPick.Click += (o, e) => + cherryPick.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CherryPick(_repo, commit)); @@ -282,7 +282,7 @@ namespace SourceGit.ViewModels var revert = new MenuItem(); revert.Header = App.Text("CommitCM.Revert"); revert.Icon = App.CreateMenuIcon("Icons.Undo"); - revert.Click += (o, e) => + revert.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Revert(_repo, commit)); @@ -294,7 +294,7 @@ namespace SourceGit.ViewModels interactiveRebase.Header = new Views.NameHighlightedTextBlock("CommitCM.InteractiveRebase", current.Name); interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase"); interactiveRebase.IsVisible = current.Head != commit.SHA; - interactiveRebase.Click += (o, e) => + interactiveRebase.Click += (_, e) => { if (_repo.WorkingCopyChangesCount > 0) { @@ -302,8 +302,12 @@ namespace SourceGit.ViewModels return; } + var toplevel = datagrid.FindAncestorOfType(); + if (toplevel == null) + return; + var dialog = new Views.InteractiveRebase() { DataContext = new InteractiveRebase(_repo, current, commit) }; - dialog.ShowDialog(App.GetTopLevel() as Window); + dialog.ShowDialog(toplevel); e.Handled = true; }; menu.Items.Add(interactiveRebase); @@ -314,7 +318,7 @@ namespace SourceGit.ViewModels var checkoutCommit = new MenuItem(); checkoutCommit.Header = App.Text("CommitCM.Checkout"); checkoutCommit.Icon = App.CreateMenuIcon("Icons.Detached"); - checkoutCommit.Click += (o, e) => + checkoutCommit.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CheckoutCommit(_repo, commit)); @@ -330,7 +334,7 @@ namespace SourceGit.ViewModels var compareWithHead = new MenuItem(); compareWithHead.Header = App.Text("CommitCM.CompareWithHead"); compareWithHead.Icon = App.CreateMenuIcon("Icons.Compare"); - compareWithHead.Click += (o, e) => + compareWithHead.Click += (_, e) => { var head = _commits.Find(x => x.SHA == current.Head); if (head == null) @@ -354,7 +358,7 @@ namespace SourceGit.ViewModels var compareWithWorktree = new MenuItem(); compareWithWorktree.Header = App.Text("CommitCM.CompareWithWorktree"); compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); - compareWithWorktree.Click += (o, e) => + compareWithWorktree.Click += (_, e) => { DetailContext = new RevisionCompare(_repo.FullPath, commit, null); e.Handled = true; @@ -368,7 +372,7 @@ namespace SourceGit.ViewModels var createBranch = new MenuItem(); createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); - createBranch.Click += (o, e) => + createBranch.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(_repo, commit)); @@ -379,7 +383,7 @@ namespace SourceGit.ViewModels var createTag = new MenuItem(); createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add"); createTag.Header = App.Text("CreateTag"); - createTag.Click += (o, e) => + createTag.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(_repo, commit)); @@ -413,7 +417,7 @@ namespace SourceGit.ViewModels var archive = new MenuItem(); archive.Icon = App.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); - archive.Click += (o, e) => + archive.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(_repo, commit)); @@ -425,7 +429,7 @@ namespace SourceGit.ViewModels var copySHA = new MenuItem(); copySHA.Header = App.Text("CommitCM.CopySHA"); copySHA.Icon = App.CreateMenuIcon("Icons.Copy"); - copySHA.Click += (o, e) => + copySHA.Click += (_, e) => { App.CopyText(commit.SHA); e.Handled = true; @@ -448,7 +452,7 @@ namespace SourceGit.ViewModels fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream); fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = !string.IsNullOrEmpty(current.UpstreamTrackStatus) && current.UpstreamTrackStatus.IndexOf('↑') < 0; - fastForward.Click += (o, e) => + fastForward.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Merge(_repo, upstream, current.Name)); @@ -459,7 +463,7 @@ namespace SourceGit.ViewModels var pull = new MenuItem(); pull.Header = new Views.NameHighlightedTextBlock("BranchCM.Pull", upstream); pull.Icon = App.CreateMenuIcon("Icons.Pull"); - pull.Click += (o, e) => + pull.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(_repo, null)); @@ -472,7 +476,7 @@ namespace SourceGit.ViewModels push.Header = new Views.NameHighlightedTextBlock("BranchCM.Push", current.Name); push.Icon = App.CreateMenuIcon("Icons.Push"); push.IsEnabled = _repo.Remotes.Count > 0; - push.Click += (o, e) => + push.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Push(_repo, current)); @@ -487,7 +491,7 @@ namespace SourceGit.ViewModels var finish = new MenuItem(); finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", current.Name); finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); - finish.Click += (o, e) => + finish.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, current, detect.Type, detect.Prefix)); @@ -500,7 +504,7 @@ namespace SourceGit.ViewModels var rename = new MenuItem(); rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", current.Name); rename.Icon = App.CreateMenuIcon("Icons.Rename"); - rename.Click += (o, e) => + rename.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, current)); @@ -520,7 +524,7 @@ namespace SourceGit.ViewModels var checkout = new MenuItem(); checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name); checkout.Icon = App.CreateMenuIcon("Icons.Check"); - checkout.Click += (o, e) => + checkout.Click += (_, e) => { _repo.CheckoutBranch(branch); e.Handled = true; @@ -531,7 +535,7 @@ namespace SourceGit.ViewModels merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name); merge.Icon = App.CreateMenuIcon("Icons.Merge"); merge.IsEnabled = !merged; - merge.Click += (o, e) => + merge.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, branch.Name, current.Name)); @@ -546,7 +550,7 @@ namespace SourceGit.ViewModels var finish = new MenuItem(); finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name); finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); - finish.Click += (o, e) => + finish.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(_repo, branch, detect.Type, detect.Prefix)); @@ -559,7 +563,7 @@ namespace SourceGit.ViewModels var rename = new MenuItem(); rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name); rename.Icon = App.CreateMenuIcon("Icons.Rename"); - rename.Click += (o, e) => + rename.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(_repo, branch)); @@ -570,7 +574,7 @@ namespace SourceGit.ViewModels var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", branch.Name); delete.Icon = App.CreateMenuIcon("Icons.Clear"); - delete.Click += (o, e) => + delete.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch)); @@ -592,7 +596,7 @@ namespace SourceGit.ViewModels var checkout = new MenuItem(); checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", name); checkout.Icon = App.CreateMenuIcon("Icons.Check"); - checkout.Click += (o, e) => + checkout.Click += (_, e) => { _repo.CheckoutBranch(branch); e.Handled = true; @@ -603,7 +607,7 @@ namespace SourceGit.ViewModels merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", name, current.Name); merge.Icon = App.CreateMenuIcon("Icons.Merge"); merge.IsEnabled = !merged; - merge.Click += (o, e) => + merge.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(_repo, name, current.Name)); @@ -616,7 +620,7 @@ namespace SourceGit.ViewModels var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name); delete.Icon = App.CreateMenuIcon("Icons.Clear"); - delete.Click += (o, e) => + delete.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(_repo, branch)); @@ -638,7 +642,7 @@ namespace SourceGit.ViewModels push.Header = new Views.NameHighlightedTextBlock("TagCM.Push", tag.Name); push.Icon = App.CreateMenuIcon("Icons.Push"); push.IsEnabled = _repo.Remotes.Count > 0; - push.Click += (o, e) => + push.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new PushTag(_repo, tag)); @@ -649,7 +653,7 @@ namespace SourceGit.ViewModels var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name); delete.Icon = App.CreateMenuIcon("Icons.Clear"); - delete.Click += (o, e) => + delete.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(_repo, tag)); diff --git a/src/ViewModels/Init.cs b/src/ViewModels/Init.cs index 2e582ccc..96b9d01f 100644 --- a/src/ViewModels/Init.cs +++ b/src/ViewModels/Init.cs @@ -38,7 +38,7 @@ namespace SourceGit.ViewModels }); } - private string _targetPath = string.Empty; + private string _targetPath = null; private RepositoryNode _parentNode = null; } } diff --git a/src/ViewModels/InitGitFlow.cs b/src/ViewModels/InitGitFlow.cs index 9e58f4bd..ed5ff3ce 100644 --- a/src/ViewModels/InitGitFlow.cs +++ b/src/ViewModels/InitGitFlow.cs @@ -114,8 +114,8 @@ namespace SourceGit.ViewModels }); } - private readonly Repository _repo = null; - private string _master = "master"; + private readonly Repository _repo; + private string _master; private string _develop = "develop"; private string _featurePrefix = "feature/"; private string _releasePrefix = "release/"; diff --git a/src/ViewModels/InteractiveRebase.cs b/src/ViewModels/InteractiveRebase.cs index bc6dde62..e6a0b69d 100644 --- a/src/ViewModels/InteractiveRebase.cs +++ b/src/ViewModels/InteractiveRebase.cs @@ -62,8 +62,8 @@ namespace SourceGit.ViewModels } private Models.InteractiveRebaseAction _action = Models.InteractiveRebaseAction.Pick; - private string _subject = string.Empty; - private string _fullMessage = string.Empty; + private string _subject; + private string _fullMessage; } public class InteractiveRebase : ObservableObject diff --git a/src/ViewModels/LFSLocks.cs b/src/ViewModels/LFSLocks.cs index ec4ff822..02b3e9a6 100644 --- a/src/ViewModels/LFSLocks.cs +++ b/src/ViewModels/LFSLocks.cs @@ -67,8 +67,8 @@ namespace SourceGit.ViewModels }); } - private string _repo = string.Empty; - private string _remote = string.Empty; + private string _repo; + private string _remote; private bool _isLoading = true; private bool _isEmpty = false; } diff --git a/src/ViewModels/Launcher.cs b/src/ViewModels/Launcher.cs index 394e6c71..d99020b9 100644 --- a/src/ViewModels/Launcher.cs +++ b/src/ViewModels/Launcher.cs @@ -1,6 +1,7 @@ using System; using System.IO; +using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Input; @@ -292,12 +293,6 @@ namespace SourceGit.ViewModels _activePage.Notifications.Add(notification); } - public void DismissNotification(Notification notice) - { - if (notice != null) - ActivePage?.Notifications.Remove(notice); - } - public ContextMenu CreateContextForPageTab(LauncherPage page) { if (page == null) @@ -307,7 +302,7 @@ namespace SourceGit.ViewModels var close = new MenuItem(); close.Header = App.Text("PageTabBar.Tab.Close"); close.InputGesture = KeyGesture.Parse(OperatingSystem.IsMacOS() ? "⌘+W" : "Ctrl+W"); - close.Click += (o, e) => + close.Click += (_, e) => { CloseTab(page); e.Handled = true; @@ -316,7 +311,7 @@ namespace SourceGit.ViewModels var closeOthers = new MenuItem(); closeOthers.Header = App.Text("PageTabBar.Tab.CloseOther"); - closeOthers.Click += (o, e) => + closeOthers.Click += (_, e) => { CloseOtherTabs(); e.Handled = true; @@ -325,7 +320,7 @@ namespace SourceGit.ViewModels var closeRight = new MenuItem(); closeRight.Header = App.Text("PageTabBar.Tab.CloseRight"); - closeRight.Click += (o, e) => + closeRight.Click += (_, e) => { CloseRightTabs(); e.Handled = true; @@ -342,13 +337,13 @@ namespace SourceGit.ViewModels { var icon = App.CreateMenuIcon("Icons.Bookmark"); icon.Fill = Models.Bookmarks.Brushes[i]; - icon.Stroke = App.Current.FindResource("Brush.FG1") as Brush; + icon.Stroke = Application.Current?.FindResource("Brush.FG1") as Brush; icon.StrokeThickness = i == 0 ? 1.0 : 0; var dupIdx = i; var setter = new MenuItem(); setter.Header = icon; - setter.Click += (o, e) => + setter.Click += (_, e) => { page.Node.Bookmark = dupIdx; e.Handled = true; @@ -361,7 +356,7 @@ namespace SourceGit.ViewModels var copyPath = new MenuItem(); copyPath.Header = App.Text("PageTabBar.Tab.CopyPath"); copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyPath.Click += (o, e) => + copyPath.Click += (_, e) => { page.CopyPath(); e.Handled = true; diff --git a/src/ViewModels/LayoutInfo.cs b/src/ViewModels/LayoutInfo.cs index 30be27de..73ffb215 100644 --- a/src/ViewModels/LayoutInfo.cs +++ b/src/ViewModels/LayoutInfo.cs @@ -1,8 +1,4 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -using Avalonia.Controls; +using Avalonia.Controls; using CommunityToolkit.Mvvm.ComponentModel; diff --git a/src/ViewModels/Preference.cs b/src/ViewModels/Preference.cs index 8714d23d..7580cc10 100644 --- a/src/ViewModels/Preference.cs +++ b/src/ViewModels/Preference.cs @@ -112,7 +112,7 @@ namespace SourceGit.ViewModels if (Models.AvatarManager.SelectedServer != value) { Models.AvatarManager.SelectedServer = value; - OnPropertyChanged(nameof(AvatarServer)); + OnPropertyChanged(); } } } @@ -221,7 +221,7 @@ namespace SourceGit.ViewModels if (Native.OS.GitExecutable != value) { Native.OS.GitExecutable = value; - OnPropertyChanged(nameof(GitInstallPath)); + OnPropertyChanged(); } } } @@ -233,7 +233,7 @@ namespace SourceGit.ViewModels { if (Native.OS.SetShell(value)) { - OnPropertyChanged(nameof(GitShell)); + OnPropertyChanged(); } } } @@ -252,7 +252,7 @@ namespace SourceGit.ViewModels if (Commands.AutoFetch.IsEnabled != value) { Commands.AutoFetch.IsEnabled = value; - OnPropertyChanged(nameof(GitAutoFetch)); + OnPropertyChanged(); } } } @@ -268,7 +268,7 @@ namespace SourceGit.ViewModels if (Commands.AutoFetch.Interval != value) { Commands.AutoFetch.Interval = (int)value; - OnPropertyChanged(nameof(GitAutoFetchInterval)); + OnPropertyChanged(); } } } diff --git a/src/ViewModels/Push.cs b/src/ViewModels/Push.cs index 12adca35..e80c7c78 100644 --- a/src/ViewModels/Push.cs +++ b/src/ViewModels/Push.cs @@ -114,7 +114,7 @@ namespace SourceGit.ViewModels } // Find preferred remote if selected local branch has upstream. - if (!string.IsNullOrEmpty(_selectedLocalBranch.Upstream)) + if (!string.IsNullOrEmpty(_selectedLocalBranch?.Upstream)) { foreach (var branch in repo.Branches) { diff --git a/src/ViewModels/Rebase.cs b/src/ViewModels/Rebase.cs index 79dd9fab..120b2f37 100644 --- a/src/ViewModels/Rebase.cs +++ b/src/ViewModels/Rebase.cs @@ -55,7 +55,7 @@ namespace SourceGit.ViewModels }); } - private readonly Repository _repo = null; - private readonly string _revision = string.Empty; + private readonly Repository _repo; + private readonly string _revision; } } diff --git a/src/ViewModels/RenameBranch.cs b/src/ViewModels/RenameBranch.cs index 8a03bf05..13428288 100644 --- a/src/ViewModels/RenameBranch.cs +++ b/src/ViewModels/RenameBranch.cs @@ -60,7 +60,7 @@ namespace SourceGit.ViewModels }); } - private readonly Repository _repo = null; - private string _name = string.Empty; + private readonly Repository _repo; + private string _name; } } diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index b42d9681..6e0c9dc4 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -129,7 +129,6 @@ namespace SourceGit.ViewModels public RepositorySettings Settings { get => _settings; - private set => SetProperty(ref _settings, value); } public int SelectedViewIndex @@ -436,7 +435,7 @@ namespace SourceGit.ViewModels var item = new MenuItem(); item.Header = App.Text("Repository.OpenIn", dupTool.Name); item.Icon = new Image { Width = 16, Height = 16, Source = dupTool.IconImage }; - item.Click += (o, e) => + item.Click += (_, e) => { dupTool.Open(_fullpath); e.Handled = true; @@ -1013,7 +1012,7 @@ namespace SourceGit.ViewModels var startFeature = new MenuItem(); startFeature.Header = App.Text("GitFlow.StartFeature"); startFeature.Icon = App.CreateMenuIcon("Icons.GitFlow.Feature"); - startFeature.Click += (o, e) => + startFeature.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowStart(this, "feature")); @@ -1023,7 +1022,7 @@ namespace SourceGit.ViewModels var startRelease = new MenuItem(); startRelease.Header = App.Text("GitFlow.StartRelease"); startRelease.Icon = App.CreateMenuIcon("Icons.GitFlow.Release"); - startRelease.Click += (o, e) => + startRelease.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowStart(this, "release")); @@ -1033,7 +1032,7 @@ namespace SourceGit.ViewModels var startHotfix = new MenuItem(); startHotfix.Header = App.Text("GitFlow.StartHotfix"); startHotfix.Icon = App.CreateMenuIcon("Icons.GitFlow.Hotfix"); - startHotfix.Click += (o, e) => + startHotfix.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowStart(this, "hotfix")); @@ -1049,7 +1048,7 @@ namespace SourceGit.ViewModels var init = new MenuItem(); init.Header = App.Text("GitFlow.Init"); init.Icon = App.CreateMenuIcon("Icons.Init"); - init.Click += (o, e) => + init.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new InitGitFlow(this)); @@ -1071,7 +1070,7 @@ namespace SourceGit.ViewModels var addPattern = new MenuItem(); addPattern.Header = App.Text("GitLFS.AddTrackPattern"); addPattern.Icon = App.CreateMenuIcon("Icons.File.Add"); - addPattern.Click += (o, e) => + addPattern.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new LFSTrackCustomPattern(this)); @@ -1085,7 +1084,7 @@ namespace SourceGit.ViewModels fetch.Header = App.Text("GitLFS.Fetch"); fetch.Icon = App.CreateMenuIcon("Icons.Fetch"); fetch.IsEnabled = Remotes.Count > 0; - fetch.Click += (o, e) => + fetch.Click += (_, e) => { if (PopupHost.CanCreatePopup()) { @@ -1103,7 +1102,7 @@ namespace SourceGit.ViewModels pull.Header = App.Text("GitLFS.Pull"); pull.Icon = App.CreateMenuIcon("Icons.Pull"); pull.IsEnabled = Remotes.Count > 0; - pull.Click += (o, e) => + pull.Click += (_, e) => { if (PopupHost.CanCreatePopup()) { @@ -1121,7 +1120,7 @@ namespace SourceGit.ViewModels push.Header = App.Text("GitLFS.Push"); push.Icon = App.CreateMenuIcon("Icons.Push"); push.IsEnabled = Remotes.Count > 0; - push.Click += (o, e) => + push.Click += (_, e) => { if (PopupHost.CanCreatePopup()) { @@ -1138,7 +1137,7 @@ namespace SourceGit.ViewModels var prune = new MenuItem(); prune.Header = App.Text("GitLFS.Prune"); prune.Icon = App.CreateMenuIcon("Icons.Clean"); - prune.Click += (o, e) => + prune.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new LFSPrune(this)); @@ -1154,10 +1153,14 @@ namespace SourceGit.ViewModels locks.IsEnabled = Remotes.Count > 0; if (Remotes.Count == 1) { - locks.Click += (o, e) => + locks.Click += (_, e) => { + var topLevel = App.GetTopLevel() as Window; + if (topLevel == null) + return; + var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, Remotes[0].Name) }; - dialog.Show(App.GetTopLevel() as Window); + dialog.Show(topLevel); e.Handled = true; }; } @@ -1168,10 +1171,14 @@ namespace SourceGit.ViewModels var remoteName = remote.Name; var lockRemote = new MenuItem(); lockRemote.Header = remoteName; - lockRemote.Click += (o, e) => + lockRemote.Click += (_, e) => { + var topLevel = App.GetTopLevel() as Window; + if (topLevel == null) + return; + var dialog = new Views.LFSLocks() { DataContext = new LFSLocks(_fullpath, remoteName) }; - dialog.Show(App.GetTopLevel() as Window); + dialog.Show(topLevel); e.Handled = true; }; locks.Items.Add(lockRemote); @@ -1186,7 +1193,7 @@ namespace SourceGit.ViewModels var install = new MenuItem(); install.Header = App.Text("GitLFS.Install"); install.Icon = App.CreateMenuIcon("Icons.Init"); - install.Click += (o, e) => + install.Click += (_, e) => { var succ = new Commands.LFS(_fullpath).Install(); if (succ) @@ -1221,7 +1228,7 @@ namespace SourceGit.ViewModels discard.Header = App.Text("BranchCM.DiscardAll"); discard.Icon = App.CreateMenuIcon("Icons.Undo"); discard.IsEnabled = _workingCopy.Count > 0; - discard.Click += (o, e) => + discard.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Discard(this)); @@ -1238,7 +1245,7 @@ namespace SourceGit.ViewModels fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream); fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0; - fastForward.Click += (o, e) => + fastForward.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Merge(this, upstream, branch.Name)); @@ -1248,7 +1255,7 @@ namespace SourceGit.ViewModels var pull = new MenuItem(); pull.Header = new Views.NameHighlightedTextBlock("BranchCM.Pull", upstream); pull.Icon = App.CreateMenuIcon("Icons.Pull"); - pull.Click += (o, e) => + pull.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(this, null)); @@ -1275,7 +1282,7 @@ namespace SourceGit.ViewModels var checkout = new MenuItem(); checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", branch.Name); checkout.Icon = App.CreateMenuIcon("Icons.Check"); - checkout.Click += (o, e) => + checkout.Click += (_, e) => { CheckoutBranch(branch); e.Handled = true; @@ -1289,7 +1296,7 @@ namespace SourceGit.ViewModels fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream.FriendlyName); fastForward.Icon = App.CreateMenuIcon("Icons.FastForward"); fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0; - fastForward.Click += (o, e) => + fastForward.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new FastForwardWithoutCheckout(this, branch, upstream)); @@ -1306,7 +1313,7 @@ namespace SourceGit.ViewModels var merge = new MenuItem(); merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", branch.Name, current.Name); merge.Icon = App.CreateMenuIcon("Icons.Merge"); - merge.Click += (o, e) => + merge.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, branch.Name, current.Name)); @@ -1316,7 +1323,7 @@ namespace SourceGit.ViewModels var rebase = new MenuItem(); rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", current.Name, branch.Name); rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); - rebase.Click += (o, e) => + rebase.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch)); @@ -1331,7 +1338,7 @@ namespace SourceGit.ViewModels var compareWithWorktree = new MenuItem(); compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree"); compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); - compareWithWorktree.Click += (o, e) => + compareWithWorktree.Click += (_, _) => { SearchResultSelectedCommit = null; @@ -1362,7 +1369,7 @@ namespace SourceGit.ViewModels var finish = new MenuItem(); finish.Header = new Views.NameHighlightedTextBlock("BranchCM.Finish", branch.Name); finish.Icon = App.CreateMenuIcon("Icons.GitFlow"); - finish.Click += (o, e) => + finish.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new GitFlowFinish(this, branch, detect.Type, detect.Prefix)); @@ -1375,7 +1382,7 @@ namespace SourceGit.ViewModels var rename = new MenuItem(); rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name); rename.Icon = App.CreateMenuIcon("Icons.Rename"); - rename.Click += (o, e) => + rename.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RenameBranch(this, branch)); @@ -1386,7 +1393,7 @@ namespace SourceGit.ViewModels delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", branch.Name); delete.Icon = App.CreateMenuIcon("Icons.Clear"); delete.IsEnabled = !branch.IsCurrent; - delete.Click += (o, e) => + delete.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(this, branch)); @@ -1396,7 +1403,7 @@ namespace SourceGit.ViewModels var createBranch = new MenuItem(); createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); - createBranch.Click += (o, e) => + createBranch.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, branch)); @@ -1406,7 +1413,7 @@ namespace SourceGit.ViewModels var createTag = new MenuItem(); createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add"); createTag.Header = App.Text("CreateTag"); - createTag.Click += (o, e) => + createTag.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(this, branch)); @@ -1442,7 +1449,7 @@ namespace SourceGit.ViewModels if (branch.Upstream == b.FullName) target.Icon = App.CreateMenuIcon("Icons.Check"); - target.Click += (o, e) => + target.Click += (_, e) => { if (Commands.Branch.SetUpstream(_fullpath, branch.Name, upstream)) Task.Run(RefreshBranches); @@ -1471,7 +1478,7 @@ namespace SourceGit.ViewModels var archive = new MenuItem(); archive.Icon = App.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); - archive.Click += (o, e) => + archive.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, branch)); @@ -1483,7 +1490,7 @@ namespace SourceGit.ViewModels var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); copy.Icon = App.CreateMenuIcon("Icons.Copy"); - copy.Click += (o, e) => + copy.Click += (_, e) => { App.CopyText(branch.Name); e.Handled = true; @@ -1502,7 +1509,7 @@ namespace SourceGit.ViewModels var visit = new MenuItem(); visit.Header = App.Text("RemoteCM.OpenInBrowser"); visit.Icon = App.CreateMenuIcon("Icons.OpenWith"); - visit.Click += (o, e) => + visit.Click += (_, e) => { Native.OS.OpenBrowser(visitURL); e.Handled = true; @@ -1515,7 +1522,7 @@ namespace SourceGit.ViewModels var fetch = new MenuItem(); fetch.Header = App.Text("RemoteCM.Fetch"); fetch.Icon = App.CreateMenuIcon("Icons.Fetch"); - fetch.Click += (o, e) => + fetch.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new Fetch(this, remote)); @@ -1525,7 +1532,7 @@ namespace SourceGit.ViewModels var prune = new MenuItem(); prune.Header = App.Text("RemoteCM.Prune"); prune.Icon = App.CreateMenuIcon("Icons.Clean"); - prune.Click += (o, e) => + prune.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowAndStartPopup(new PruneRemote(this, remote)); @@ -1535,7 +1542,7 @@ namespace SourceGit.ViewModels var edit = new MenuItem(); edit.Header = App.Text("RemoteCM.Edit"); edit.Icon = App.CreateMenuIcon("Icons.Edit"); - edit.Click += (o, e) => + edit.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new EditRemote(this, remote)); @@ -1545,7 +1552,7 @@ namespace SourceGit.ViewModels var delete = new MenuItem(); delete.Header = App.Text("RemoteCM.Delete"); delete.Icon = App.CreateMenuIcon("Icons.Clear"); - delete.Click += (o, e) => + delete.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteRemote(this, remote)); @@ -1555,7 +1562,7 @@ namespace SourceGit.ViewModels var copy = new MenuItem(); copy.Header = App.Text("RemoteCM.CopyURL"); copy.Icon = App.CreateMenuIcon("Icons.Copy"); - copy.Click += (o, e) => + copy.Click += (_, e) => { App.CopyText(remote.URL); e.Handled = true; @@ -1580,7 +1587,7 @@ namespace SourceGit.ViewModels var checkout = new MenuItem(); checkout.Header = new Views.NameHighlightedTextBlock("BranchCM.Checkout", name); checkout.Icon = App.CreateMenuIcon("Icons.Check"); - checkout.Click += (o, e) => + checkout.Click += (_, e) => { CheckoutBranch(branch); e.Handled = true; @@ -1593,7 +1600,7 @@ namespace SourceGit.ViewModels var pull = new MenuItem(); pull.Header = new Views.NameHighlightedTextBlock("BranchCM.PullInto", name, current.Name); pull.Icon = App.CreateMenuIcon("Icons.Pull"); - pull.Click += (o, e) => + pull.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Pull(this, branch)); @@ -1603,7 +1610,7 @@ namespace SourceGit.ViewModels var merge = new MenuItem(); merge.Header = new Views.NameHighlightedTextBlock("BranchCM.Merge", name, current.Name); merge.Icon = App.CreateMenuIcon("Icons.Merge"); - merge.Click += (o, e) => + merge.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Merge(this, name, current.Name)); @@ -1613,7 +1620,7 @@ namespace SourceGit.ViewModels var rebase = new MenuItem(); rebase.Header = new Views.NameHighlightedTextBlock("BranchCM.Rebase", current.Name, name); rebase.Icon = App.CreateMenuIcon("Icons.Rebase"); - rebase.Click += (o, e) => + rebase.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Rebase(this, current, branch)); @@ -1632,7 +1639,7 @@ namespace SourceGit.ViewModels var compareWithWorktree = new MenuItem(); compareWithWorktree.Header = App.Text("BranchCM.CompareWithWorktree"); compareWithWorktree.Icon = App.CreateMenuIcon("Icons.Compare"); - compareWithWorktree.Click += (o, e) => + compareWithWorktree.Click += (_, _) => { SearchResultSelectedCommit = null; @@ -1660,7 +1667,7 @@ namespace SourceGit.ViewModels var delete = new MenuItem(); delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name); delete.Icon = App.CreateMenuIcon("Icons.Clear"); - delete.Click += (o, e) => + delete.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteBranch(this, branch)); @@ -1670,7 +1677,7 @@ namespace SourceGit.ViewModels var createBranch = new MenuItem(); createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); - createBranch.Click += (o, e) => + createBranch.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, branch)); @@ -1680,7 +1687,7 @@ namespace SourceGit.ViewModels var createTag = new MenuItem(); createTag.Icon = App.CreateMenuIcon("Icons.Tag.Add"); createTag.Header = App.Text("CreateTag"); - createTag.Click += (o, e) => + createTag.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateTag(this, branch)); @@ -1690,7 +1697,7 @@ namespace SourceGit.ViewModels var archive = new MenuItem(); archive.Icon = App.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); - archive.Click += (o, e) => + archive.Click += (_, e) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, branch)); @@ -1700,7 +1707,7 @@ namespace SourceGit.ViewModels var copy = new MenuItem(); copy.Header = App.Text("BranchCM.CopyName"); copy.Icon = App.CreateMenuIcon("Icons.Copy"); - copy.Click += (o, e) => + copy.Click += (_, e) => { App.CopyText(name); e.Handled = true; @@ -1722,7 +1729,7 @@ namespace SourceGit.ViewModels var createBranch = new MenuItem(); createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add"); createBranch.Header = App.Text("CreateBranch"); - createBranch.Click += (o, ev) => + createBranch.Click += (_, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new CreateBranch(this, tag)); @@ -1733,7 +1740,7 @@ namespace SourceGit.ViewModels pushTag.Header = new Views.NameHighlightedTextBlock("TagCM.Push", tag.Name); pushTag.Icon = App.CreateMenuIcon("Icons.Push"); pushTag.IsEnabled = Remotes.Count > 0; - pushTag.Click += (o, ev) => + pushTag.Click += (_, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new PushTag(this, tag)); @@ -1743,7 +1750,7 @@ namespace SourceGit.ViewModels var deleteTag = new MenuItem(); deleteTag.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name); deleteTag.Icon = App.CreateMenuIcon("Icons.Clear"); - deleteTag.Click += (o, ev) => + deleteTag.Click += (_, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteTag(this, tag)); @@ -1753,7 +1760,7 @@ namespace SourceGit.ViewModels var archive = new MenuItem(); archive.Icon = App.CreateMenuIcon("Icons.Archive"); archive.Header = App.Text("Archive"); - archive.Click += (o, ev) => + archive.Click += (_, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new Archive(this, tag)); @@ -1763,7 +1770,7 @@ namespace SourceGit.ViewModels var copy = new MenuItem(); copy.Header = App.Text("TagCM.Copy"); copy.Icon = App.CreateMenuIcon("Icons.Copy"); - copy.Click += (o, ev) => + copy.Click += (_, ev) => { App.CopyText(tag.Name); ev.Handled = true; @@ -1786,7 +1793,7 @@ namespace SourceGit.ViewModels var open = new MenuItem(); open.Header = App.Text("Submodule.Open"); open.Icon = App.CreateMenuIcon("Icons.Folder.Open"); - open.Click += (o, ev) => + open.Click += (_, ev) => { OpenSubmodule(submodule); ev.Handled = true; @@ -1795,7 +1802,7 @@ namespace SourceGit.ViewModels var copy = new MenuItem(); copy.Header = App.Text("Submodule.CopyPath"); copy.Icon = App.CreateMenuIcon("Icons.Copy"); - copy.Click += (o, ev) => + copy.Click += (_, ev) => { App.CopyText(submodule); ev.Handled = true; @@ -1804,7 +1811,7 @@ namespace SourceGit.ViewModels var rm = new MenuItem(); rm.Header = App.Text("Submodule.Remove"); rm.Icon = App.CreateMenuIcon("Icons.Clear"); - rm.Click += (o, ev) => + rm.Click += (_, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DeleteSubmodule(this, submodule)); @@ -1827,7 +1834,7 @@ namespace SourceGit.ViewModels var unlock = new MenuItem(); unlock.Header = App.Text("Worktree.Unlock"); unlock.Icon = App.CreateMenuIcon("Icons.Unlock"); - unlock.Click += (o, ev) => + unlock.Click += (_, ev) => { SetWatcherEnabled(false); var succ = new Commands.Worktree(_fullpath).Unlock(worktree.FullPath); @@ -1843,7 +1850,7 @@ namespace SourceGit.ViewModels var loc = new MenuItem(); loc.Header = App.Text("Worktree.Lock"); loc.Icon = App.CreateMenuIcon("Icons.Lock"); - loc.Click += (o, ev) => + loc.Click += (_, ev) => { SetWatcherEnabled(false); var succ = new Commands.Worktree(_fullpath).Lock(worktree.FullPath); @@ -1858,7 +1865,7 @@ namespace SourceGit.ViewModels var remove = new MenuItem(); remove.Header = App.Text("Worktree.Remove"); remove.Icon = App.CreateMenuIcon("Icons.Clear"); - remove.Click += (o, ev) => + remove.Click += (_, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new RemoveWorktree(this, worktree)); @@ -1869,7 +1876,7 @@ namespace SourceGit.ViewModels var copy = new MenuItem(); copy.Header = App.Text("Worktree.CopyPath"); copy.Icon = App.CreateMenuIcon("Icons.Copy"); - copy.Click += (o, e) => + copy.Click += (_, e) => { App.CopyText(worktree.FullPath); e.Handled = true; @@ -1899,12 +1906,16 @@ namespace SourceGit.ViewModels target.Icon = App.CreateMenuIcon(b.IsCurrent ? "Icons.Check" : "Icons.Branch"); target.Click += (_, e) => { + var topLevel = App.GetTopLevel() as Window; + if (topLevel == null) + return; + var wnd = new Views.BranchCompare() { DataContext = new BranchCompare(FullPath, branch, dup) }; - wnd.Show(App.GetTopLevel() as Window); + wnd.Show(topLevel); e.Handled = true; }; diff --git a/src/ViewModels/RevisionCompare.cs b/src/ViewModels/RevisionCompare.cs index 2f288ab6..44683ab7 100644 --- a/src/ViewModels/RevisionCompare.cs +++ b/src/ViewModels/RevisionCompare.cs @@ -209,8 +209,8 @@ namespace SourceGit.ViewModels } } - private string _repo = string.Empty; - private string _endPoint = string.Empty; + private string _repo; + private string _endPoint; private List _changes = null; private List _visibleChanges = null; private List _selectedChanges = null; diff --git a/src/ViewModels/Reword.cs b/src/ViewModels/Reword.cs index b3f0e9f8..ce2f4d6c 100644 --- a/src/ViewModels/Reword.cs +++ b/src/ViewModels/Reword.cs @@ -45,8 +45,8 @@ namespace SourceGit.ViewModels }); } - private readonly Repository _repo = null; - private string _message = string.Empty; - private string _oldMessage = string.Empty; + private readonly Repository _repo; + private string _message; + private string _oldMessage; } } diff --git a/src/ViewModels/Squash.cs b/src/ViewModels/Squash.cs index 8e78658d..a24f5152 100644 --- a/src/ViewModels/Squash.cs +++ b/src/ViewModels/Squash.cs @@ -49,7 +49,7 @@ namespace SourceGit.ViewModels }); } - private readonly Repository _repo = null; - private string _message = string.Empty; + private readonly Repository _repo; + private string _message; } } diff --git a/src/ViewModels/StashChanges.cs b/src/ViewModels/StashChanges.cs index 32b07900..cdeecf7b 100644 --- a/src/ViewModels/StashChanges.cs +++ b/src/ViewModels/StashChanges.cs @@ -5,7 +5,6 @@ namespace SourceGit.ViewModels { public class StashChanges : Popup { - public string Message { get; diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index 9071300a..e07ac432 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -119,7 +119,7 @@ namespace SourceGit.ViewModels var apply = new MenuItem(); apply.Header = App.Text("StashCM.Apply"); - apply.Click += (o, ev) => + apply.Click += (_, ev) => { Task.Run(() => new Commands.Stash(_repo.FullPath).Apply(stash.Name)); ev.Handled = true; @@ -127,7 +127,7 @@ namespace SourceGit.ViewModels var pop = new MenuItem(); pop.Header = App.Text("StashCM.Pop"); - pop.Click += (o, ev) => + pop.Click += (_, ev) => { Task.Run(() => new Commands.Stash(_repo.FullPath).Pop(stash.Name)); ev.Handled = true; @@ -135,7 +135,7 @@ namespace SourceGit.ViewModels var drop = new MenuItem(); drop.Header = App.Text("StashCM.Drop"); - drop.Click += (o, ev) => + drop.Click += (_, ev) => { if (PopupHost.CanCreatePopup()) PopupHost.ShowPopup(new DropStash(_repo.FullPath, stash)); diff --git a/src/ViewModels/Statistics.cs b/src/ViewModels/Statistics.cs index c01f2833..44cec313 100644 --- a/src/ViewModels/Statistics.cs +++ b/src/ViewModels/Statistics.cs @@ -32,11 +32,9 @@ namespace SourceGit.ViewModels public Statistics(string repo) { - _repo = repo; - Task.Run(() => { - var result = new Commands.Statistics(_repo).Result(); + var result = new Commands.Statistics(repo).Result(); Dispatcher.UIThread.Invoke(() => { _data = result; @@ -65,7 +63,6 @@ namespace SourceGit.ViewModels } } - private readonly string _repo = string.Empty; private bool _isLoading = true; private Models.Statistics _data = null; private Models.StatisticsReport _selectedReport = null; diff --git a/src/ViewModels/TwoSideTextDiff.cs b/src/ViewModels/TwoSideTextDiff.cs index 8d2aedcc..a3cab886 100644 --- a/src/ViewModels/TwoSideTextDiff.cs +++ b/src/ViewModels/TwoSideTextDiff.cs @@ -6,7 +6,7 @@ namespace SourceGit.ViewModels { public class TwoSideTextDiff : ObservableObject { - public string File { get; set; } = string.Empty; + public string File { get; set; } public List Old { get; set; } = new List(); public List New { get; set; } = new List(); public int MaxLineNumber = 0; diff --git a/src/ViewModels/Welcome.cs b/src/ViewModels/Welcome.cs index bb5690a7..c4bed3cb 100644 --- a/src/ViewModels/Welcome.cs +++ b/src/ViewModels/Welcome.cs @@ -1,5 +1,5 @@ using System; - +using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; @@ -49,13 +49,10 @@ namespace SourceGit.ViewModels return; } - if (PopupHost.CanCreatePopup()) + if (PopupHost.CanCreatePopup() && + Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { DataContext: Launcher launcher } }) { - if (App.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - var launcher = desktop.MainWindow.DataContext as Launcher; - PopupHost.ShowPopup(new Clone(launcher)); - } + PopupHost.ShowPopup(new Clone(launcher)); } } @@ -98,9 +95,9 @@ namespace SourceGit.ViewModels openAll.Icon = App.CreateMenuIcon("Icons.Folder.Open"); openAll.Click += (_, e) => { - if (App.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (PopupHost.CanCreatePopup() && + Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { DataContext: Launcher launcher } }) { - var launcher = desktop.MainWindow.DataContext as Launcher; OpenAllInNode(launcher, node); } diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index ef4032a3..7566ea53 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -44,7 +44,7 @@ namespace SourceGit.ViewModels if (_repo.IncludeUntracked != value) { _repo.IncludeUntracked = value; - OnPropertyChanged(nameof(IncludeUntracked)); + OnPropertyChanged(); } } } @@ -286,12 +286,16 @@ namespace SourceGit.ViewModels public void OpenAssumeUnchanged() { + var toplevel = App.GetTopLevel() as Window; + if (toplevel == null) + return; + var dialog = new Views.AssumeUnchangedManager() { DataContext = new AssumeUnchangedManager(_repo.FullPath) }; - dialog.ShowDialog(App.GetTopLevel() as Window); + dialog.ShowDialog(toplevel); } public void StageSelected() @@ -634,7 +638,7 @@ namespace SourceGit.ViewModels lfsTrackByExtension.Header = App.Text("GitLFS.TrackByExtension", extension); lfsTrackByExtension.Click += async (_, e) => { - var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track("*" + extension, false)); + var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track("*" + extension)); if (succ) App.SendNotification(_repo.FullPath, $"Tracking all *{extension} files successfully!"); @@ -652,7 +656,7 @@ namespace SourceGit.ViewModels lfsLock.IsEnabled = _repo.Remotes.Count > 0; if (_repo.Remotes.Count == 1) { - lfsLock.Click += async (o, e) => + lfsLock.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(_repo.Remotes[0].Name, change.Path)); if (succ) @@ -668,7 +672,7 @@ namespace SourceGit.ViewModels var remoteName = remote.Name; var lockRemote = new MenuItem(); lockRemote.Header = remoteName; - lockRemote.Click += async (o, e) => + lockRemote.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(remoteName, change.Path)); if (succ) @@ -687,7 +691,7 @@ namespace SourceGit.ViewModels lfsUnlock.IsEnabled = _repo.Remotes.Count > 0; if (_repo.Remotes.Count == 1) { - lfsUnlock.Click += async (o, e) => + lfsUnlock.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(_repo.Remotes[0].Name, change.Path, false)); if (succ) @@ -703,7 +707,7 @@ namespace SourceGit.ViewModels var remoteName = remote.Name; var unlockRemote = new MenuItem(); unlockRemote.Header = remoteName; - unlockRemote.Click += async (o, e) => + unlockRemote.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(remoteName, change.Path, false)); if (succ) @@ -823,7 +827,7 @@ namespace SourceGit.ViewModels var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); patch.Icon = App.CreateMenuIcon("Icons.Diff"); - patch.Click += async (o, e) => + patch.Click += async (_, e) => { var topLevel = App.GetTopLevel(); if (topLevel == null) @@ -869,7 +873,7 @@ namespace SourceGit.ViewModels explore.IsEnabled = File.Exists(path) || Directory.Exists(path); explore.Header = App.Text("RevealFile"); explore.Icon = App.CreateMenuIcon("Icons.Folder.Open"); - explore.Click += (o, e) => + explore.Click += (_, e) => { Native.OS.OpenInFileManager(path, true); e.Handled = true; @@ -888,7 +892,7 @@ namespace SourceGit.ViewModels var unstage = new MenuItem(); unstage.Header = App.Text("FileCM.Unstage"); unstage.Icon = App.CreateMenuIcon("Icons.File.Remove"); - unstage.Click += (o, e) => + unstage.Click += (_, e) => { UnstageChanges(_selectedStaged); e.Handled = true; @@ -917,7 +921,7 @@ namespace SourceGit.ViewModels var patch = new MenuItem(); patch.Header = App.Text("FileCM.SaveAsPatch"); patch.Icon = App.CreateMenuIcon("Icons.Diff"); - patch.Click += async (o, e) => + patch.Click += async (_, e) => { var topLevel = App.GetTopLevel(); if (topLevel == null) @@ -942,7 +946,7 @@ namespace SourceGit.ViewModels var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); - copyPath.Click += (o, e) => + copyPath.Click += (_, e) => { App.CopyText(change.Path); e.Handled = true; @@ -979,7 +983,7 @@ namespace SourceGit.ViewModels lfsLock.IsEnabled = _repo.Remotes.Count > 0; if (_repo.Remotes.Count == 1) { - lfsLock.Click += async (o, e) => + lfsLock.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(_repo.Remotes[0].Name, change.Path)); if (succ) @@ -995,7 +999,7 @@ namespace SourceGit.ViewModels var remoteName = remote.Name; var lockRemote = new MenuItem(); lockRemote.Header = remoteName; - lockRemote.Click += async (o, e) => + lockRemote.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Lock(remoteName, change.Path)); if (succ) @@ -1014,7 +1018,7 @@ namespace SourceGit.ViewModels lfsUnlock.IsEnabled = _repo.Remotes.Count > 0; if (_repo.Remotes.Count == 1) { - lfsUnlock.Click += async (o, e) => + lfsUnlock.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(_repo.Remotes[0].Name, change.Path, false)); if (succ) @@ -1030,7 +1034,7 @@ namespace SourceGit.ViewModels var remoteName = remote.Name; var unlockRemote = new MenuItem(); unlockRemote.Header = remoteName; - unlockRemote.Click += async (o, e) => + unlockRemote.Click += async (_, e) => { var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Unlock(remoteName, change.Path, false)); if (succ) @@ -1055,7 +1059,7 @@ namespace SourceGit.ViewModels var unstage = new MenuItem(); unstage.Header = App.Text("FileCM.UnstageMulti", _selectedStaged.Count); unstage.Icon = App.CreateMenuIcon("Icons.File.Remove"); - unstage.Click += (o, e) => + unstage.Click += (_, e) => { UnstageChanges(_selectedStaged); e.Handled = true; @@ -1140,7 +1144,7 @@ namespace SourceGit.ViewModels var item = new MenuItem(); item.Header = dump; - item.Click += (o, e) => + item.Click += (_, e) => { CommitMessage = dump; e.Handled = true; diff --git a/src/Views/Launcher.axaml b/src/Views/Launcher.axaml index b286a5c2..f8bbf93e 100644 --- a/src/Views/Launcher.axaml +++ b/src/Views/Launcher.axaml @@ -4,8 +4,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:s="using:SourceGit" xmlns:vm="using:SourceGit.ViewModels" - xmlns:m="using:SourceGit.Models" - xmlns:c="using:SourceGit.Converters" xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.Launcher" @@ -75,110 +73,14 @@ - - - + + + - - - - - - + + - - - - - - - - - - - - - - - - - - + + + + + + - - + + + + + - - + + + - + - - - - - - - - - - - - - - - - - - - - - - + + + - + + + + + + + + + + + + + - - - - - - - + + + + + - - - - - + + + + + + + + - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - + + + + + + + + + + + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - + IsVisible="{Binding IsTagGroupExpanded, Mode=OneWay}" + SelectionChanged="OnTagDataGridSelectionChanged" + ContextRequested="OnTagContextRequested" + PropertyChanged="OnLeftSidebarDataGridPropertyChanged"> + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -630,134 +361,328 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + - - - - - - - - + + + + - - - + + - - - - - - - - - - - - - - + + + + - + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/Repository.axaml.cs b/src/Views/Repository.axaml.cs index 47d1f2ae..eead96ef 100644 --- a/src/Views/Repository.axaml.cs +++ b/src/Views/Repository.axaml.cs @@ -21,48 +21,6 @@ namespace SourceGit.Views UpdateLeftSidebarLayout(); } - private void OpenWithExternalTools(object sender, RoutedEventArgs e) - { - if (sender is Button button && DataContext is ViewModels.Repository repo) - { - var menu = repo.CreateContextMenuForExternalTools(); - button.OpenContextMenu(menu); - e.Handled = true; - } - } - - private void OpenGitFlowMenu(object sender, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo) - { - var menu = repo.CreateContextMenuForGitFlow(); - (sender as Control)?.OpenContextMenu(menu); - } - - e.Handled = true; - } - - private void OpenGitLFSMenu(object sender, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo) - { - var menu = repo.CreateContextMenuForGitLFS(); - (sender as Control)?.OpenContextMenu(menu); - } - - e.Handled = true; - } - - private async void OpenStatistics(object _, RoutedEventArgs e) - { - if (DataContext is ViewModels.Repository repo && TopLevel.GetTopLevel(this) is Window owner) - { - var dialog = new Statistics() { DataContext = new ViewModels.Statistics(repo.FullPath) }; - await dialog.ShowDialog(owner); - e.Handled = true; - } - } - private void OnSearchCommitPanelPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property == IsVisibleProperty && sender is Grid { IsVisible: true }) diff --git a/src/Views/RepositoryToolbar.axaml b/src/Views/RepositoryToolbar.axaml new file mode 100644 index 00000000..3e6cff36 --- /dev/null +++ b/src/Views/RepositoryToolbar.axaml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Views/RepositoryToolbar.axaml.cs b/src/Views/RepositoryToolbar.axaml.cs new file mode 100644 index 00000000..40f90291 --- /dev/null +++ b/src/Views/RepositoryToolbar.axaml.cs @@ -0,0 +1,56 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; + +namespace SourceGit.Views +{ + public partial class RepositoryToolbar : UserControl + { + public RepositoryToolbar() + { + InitializeComponent(); + } + + private void OpenWithExternalTools(object sender, RoutedEventArgs e) + { + if (sender is Button button && DataContext is ViewModels.Repository repo) + { + var menu = repo.CreateContextMenuForExternalTools(); + button.OpenContextMenu(menu); + e.Handled = true; + } + } + + private void OpenGitFlowMenu(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.Repository repo) + { + var menu = repo.CreateContextMenuForGitFlow(); + (sender as Control)?.OpenContextMenu(menu); + } + + e.Handled = true; + } + + private void OpenGitLFSMenu(object sender, RoutedEventArgs e) + { + if (DataContext is ViewModels.Repository repo) + { + var menu = repo.CreateContextMenuForGitLFS(); + (sender as Control)?.OpenContextMenu(menu); + } + + e.Handled = true; + } + + private async void OpenStatistics(object _, RoutedEventArgs e) + { + if (DataContext is ViewModels.Repository repo && TopLevel.GetTopLevel(this) is Window owner) + { + var dialog = new Statistics() { DataContext = new ViewModels.Statistics(repo.FullPath) }; + await dialog.ShowDialog(owner); + e.Handled = true; + } + } + } +} + diff --git a/src/Views/Welcome.axaml b/src/Views/Welcome.axaml index f8e304e9..1c4fa737 100644 --- a/src/Views/Welcome.axaml +++ b/src/Views/Welcome.axaml @@ -4,37 +4,11 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:c="using:SourceGit.Converters" xmlns:vm="using:SourceGit.ViewModels" - xmlns:v="using:SourceGit.Views" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="SourceGit.Views.Welcome" x:DataType="vm:Welcome"> - - - - - - - - - - - - - - - - + + - (); - var launcher = owner?.DataContext as ViewModels.Launcher; - if (launcher == null) - return null; - - var page = launcher.ActivePage; - return Task.Run(() => + var root = new Commands.QueryRepositoryRootPath(path).Result(); + if (string.IsNullOrEmpty(root)) { - var root = new Commands.QueryRepositoryRootPath(path).Result(); - if (string.IsNullOrEmpty(root)) - { - Dispatcher.UIThread.Invoke(() => (DataContext as ViewModels.Welcome)?.InitRepository(path, parent)); - return; - } + (DataContext as ViewModels.Welcome)?.InitRepository(path, parent); + return; + } - Dispatcher.UIThread.Invoke(() => - { - var normalizedPath = root.Replace("\\", "/"); - var node = ViewModels.Preference.FindOrAddNodeByRepositoryPath(normalizedPath, parent, true); - launcher.OpenRepositoryInTab(node, page); - }); - }); + var normalizedPath = root.Replace("\\", "/"); + var node = ViewModels.Preference.FindOrAddNodeByRepositoryPath(normalizedPath, parent, true); + var launcher = this.FindAncestorOfType()?.DataContext as ViewModels.Launcher; + launcher?.OpenRepositoryInTab(node, launcher.ActivePage); } private bool _pressedTreeNode = false; diff --git a/src/Views/WelcomeToolbar.axaml b/src/Views/WelcomeToolbar.axaml new file mode 100644 index 00000000..3ef07565 --- /dev/null +++ b/src/Views/WelcomeToolbar.axaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + diff --git a/src/Views/WelcomeToolbar.axaml.cs b/src/Views/WelcomeToolbar.axaml.cs new file mode 100644 index 00000000..7294ff4f --- /dev/null +++ b/src/Views/WelcomeToolbar.axaml.cs @@ -0,0 +1,58 @@ +using System.IO; + +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; +using Avalonia.VisualTree; + +namespace SourceGit.Views +{ + public partial class WelcomeToolbar : UserControl + { + public WelcomeToolbar() + { + InitializeComponent(); + } + + private async void OpenLocalRepository(object _1, RoutedEventArgs e) + { + if (!ViewModels.PopupHost.CanCreatePopup()) + return; + + var topLevel = TopLevel.GetTopLevel(this); + if (topLevel == null) + return; + + var options = new FolderPickerOpenOptions() { AllowMultiple = false }; + var selected = await topLevel.StorageProvider.OpenFolderPickerAsync(options); + if (selected.Count == 1) + OpenOrInitRepository(selected[0].Path.LocalPath); + + e.Handled = true; + } + + private void OpenOrInitRepository(string path, ViewModels.RepositoryNode parent = null) + { + if (!Directory.Exists(path)) + { + if (File.Exists(path)) + path = Path.GetDirectoryName(path); + else + return; + } + + var root = new Commands.QueryRepositoryRootPath(path).Result(); + if (string.IsNullOrEmpty(root)) + { + (DataContext as ViewModels.Welcome)?.InitRepository(path, parent); + return; + } + + var normalizedPath = root.Replace("\\", "/"); + var node = ViewModels.Preference.FindOrAddNodeByRepositoryPath(normalizedPath, parent, true); + var launcher = this.FindAncestorOfType()?.DataContext as ViewModels.Launcher; + launcher?.OpenRepositoryInTab(node, launcher.ActivePage); + } + } +} +