mirror of
https://github.com/sourcegit-scm/sourcegit
synced 2025-06-21 02:15:00 +00:00
code_style: general cleanup
This commit is contained in:
parent
ae46728bbc
commit
dc74ebb98a
48 changed files with 123 additions and 240 deletions
|
@ -71,20 +71,20 @@ dotnet_style_predefined_type_for_member_access = true:suggestion
|
|||
|
||||
# name all constant fields using PascalCase
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
|
||||
# private static fields should have s_ prefix
|
||||
dotnet_naming_rule.private_static_fields_should_have_prefix.severity = suggestion
|
||||
dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
|
||||
dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
|
||||
dotnet_naming_rule.private_static_fields_should_have_prefix.style = private_static_prefix_style
|
||||
|
||||
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_fields.required_modifiers = static
|
||||
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private
|
||||
|
||||
|
@ -93,7 +93,7 @@ dotnet_naming_style.private_static_prefix_style.capitalization = camel_case
|
|||
|
||||
# internal and private fields should be _camelCase
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
|
||||
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
|
||||
|
|
|
@ -521,7 +521,7 @@ namespace SourceGit
|
|||
private bool TryLaunchAsCoreEditor(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var args = desktop.Args;
|
||||
if (args == null || args.Length <= 1 || !args[0].Equals("--core-editor", StringComparison.Ordinal))
|
||||
if (args is not { Length: > 1 } || !args[0].Equals("--core-editor", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
var file = args[1];
|
||||
|
|
|
@ -48,16 +48,12 @@ namespace SourceGit.Commands
|
|||
if (remoteHeads.TryGetValue(b.Upstream, out var upstreamHead))
|
||||
{
|
||||
b.IsUpstreamGone = false;
|
||||
|
||||
if (b.TrackStatus == null)
|
||||
b.TrackStatus = new QueryTrackStatus(WorkingDirectory, b.Head, upstreamHead).Result();
|
||||
b.TrackStatus ??= new QueryTrackStatus(WorkingDirectory, b.Head, upstreamHead).Result();
|
||||
}
|
||||
else
|
||||
{
|
||||
b.IsUpstreamGone = true;
|
||||
|
||||
if (b.TrackStatus == null)
|
||||
b.TrackStatus = new Models.BranchTrackStatus();
|
||||
b.TrackStatus ??= new Models.BranchTrackStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace SourceGit.Commands
|
|||
var records = rs.StdOut.Split(_boundary, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var record in records)
|
||||
{
|
||||
var subs = record.Split('\0', StringSplitOptions.None);
|
||||
var subs = record.Split('\0');
|
||||
if (subs.Length != 6)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace SourceGit.Commands
|
|||
}
|
||||
}
|
||||
|
||||
private static bool ExecCmd(string repo, string args, string outputFile, Stream input = null)
|
||||
private static void ExecCmd(string repo, string args, string outputFile, Stream input = null)
|
||||
{
|
||||
var starter = new ProcessStartInfo();
|
||||
starter.WorkingDirectory = repo;
|
||||
|
@ -45,10 +45,7 @@ namespace SourceGit.Commands
|
|||
proc.StandardInput.Write(new StreamReader(input).ReadToEnd());
|
||||
proc.StandardOutput.BaseStream.CopyTo(sw);
|
||||
proc.WaitForExit();
|
||||
var rs = proc.ExitCode == 0;
|
||||
proc.Close();
|
||||
|
||||
return rs;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -56,7 +53,6 @@ namespace SourceGit.Commands
|
|||
{
|
||||
App.RaiseException(repo, "Save file failed: " + e.Message);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,10 +26,7 @@ namespace SourceGit.Models
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = new AvatarManager();
|
||||
|
||||
return _instance;
|
||||
return _instance ??= new AvatarManager();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,10 @@ namespace SourceGit.Models
|
|||
Deleted,
|
||||
}
|
||||
|
||||
public class TextInlineRange
|
||||
public class TextInlineRange(int p, int n)
|
||||
{
|
||||
public int Start { get; set; }
|
||||
public int End { get; set; }
|
||||
public TextInlineRange(int p, int n) { Start = p; End = p + n - 1; }
|
||||
public int Start { get; set; } = p;
|
||||
public int End { get; set; } = p + n - 1;
|
||||
}
|
||||
|
||||
public class TextDiffLine
|
||||
|
@ -556,7 +555,7 @@ namespace SourceGit.Models
|
|||
}
|
||||
else if (test.Type == TextDiffLineType.Added)
|
||||
{
|
||||
if (i < start - 1)
|
||||
if (i < start - 1 || isOldSide)
|
||||
{
|
||||
if (revert)
|
||||
{
|
||||
|
@ -566,18 +565,7 @@ namespace SourceGit.Models
|
|||
}
|
||||
else
|
||||
{
|
||||
if (isOldSide)
|
||||
{
|
||||
if (revert)
|
||||
{
|
||||
newCount++;
|
||||
oldCount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newCount++;
|
||||
}
|
||||
newCount++;
|
||||
}
|
||||
|
||||
if (i == end - 1 && tailed)
|
||||
|
@ -655,9 +643,7 @@ namespace SourceGit.Models
|
|||
public string NewImageSize => New != null ? $"{New.PixelSize.Width} x {New.PixelSize.Height}" : "0 x 0";
|
||||
}
|
||||
|
||||
public class NoOrEOLChange
|
||||
{
|
||||
}
|
||||
public class NoOrEOLChange;
|
||||
|
||||
public class FileModeDiff
|
||||
{
|
||||
|
|
|
@ -107,8 +107,7 @@ namespace SourceGit.Models
|
|||
// Ignore
|
||||
}
|
||||
|
||||
if (_customPaths == null)
|
||||
_customPaths = new ExternalToolPaths();
|
||||
_customPaths ??= new ExternalToolPaths();
|
||||
}
|
||||
|
||||
public void TryAdd(string name, string icon, Func<string> finder, Func<string, string> execArgsGenerator = null)
|
||||
|
|
|
@ -8,10 +8,7 @@ namespace SourceGit.Models
|
|||
{
|
||||
public class IpcChannel : IDisposable
|
||||
{
|
||||
public bool IsFirstInstance
|
||||
{
|
||||
get => _isFirstInstance;
|
||||
}
|
||||
public bool IsFirstInstance { get; }
|
||||
|
||||
public event Action<string> MessageReceived;
|
||||
|
||||
|
@ -20,7 +17,7 @@ namespace SourceGit.Models
|
|||
try
|
||||
{
|
||||
_singletonLock = File.Open(Path.Combine(Native.OS.DataDir, "process.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
|
||||
_isFirstInstance = true;
|
||||
IsFirstInstance = true;
|
||||
_server = new NamedPipeServerStream(
|
||||
"SourceGitIPCChannel" + Environment.UserName,
|
||||
PipeDirection.In,
|
||||
|
@ -32,7 +29,7 @@ namespace SourceGit.Models
|
|||
}
|
||||
catch
|
||||
{
|
||||
_isFirstInstance = false;
|
||||
IsFirstInstance = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +94,6 @@ namespace SourceGit.Models
|
|||
}
|
||||
|
||||
private FileStream _singletonLock = null;
|
||||
private bool _isFirstInstance = false;
|
||||
private NamedPipeServerStream _server = null;
|
||||
private CancellationTokenSource _cancellationTokenSource = null;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
namespace SourceGit.Models
|
||||
{
|
||||
public class Null
|
||||
{
|
||||
}
|
||||
public class Null;
|
||||
}
|
||||
|
|
|
@ -33,9 +33,7 @@ namespace SourceGit.Models
|
|||
}
|
||||
}
|
||||
|
||||
public class AlreadyUpToDate
|
||||
{
|
||||
}
|
||||
public class AlreadyUpToDate;
|
||||
|
||||
public class SelfUpdateFailed
|
||||
{
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace SourceGit.Models
|
|||
private int? Integer()
|
||||
{
|
||||
var start = _pos;
|
||||
while (Peek() is char c && c >= '0' && c <= '9')
|
||||
while (Peek() is >= '0' and <= '9')
|
||||
{
|
||||
_pos++;
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ namespace SourceGit.Models
|
|||
// text token start
|
||||
var tok = _pos;
|
||||
bool esc = false;
|
||||
while (Next() is char c)
|
||||
while (Next() is { } c)
|
||||
{
|
||||
if (esc)
|
||||
{
|
||||
|
@ -129,7 +129,7 @@ namespace SourceGit.Models
|
|||
{
|
||||
case ESCAPE:
|
||||
// allow to escape only \ and $
|
||||
if (Peek() is char nc && (nc == ESCAPE || nc == VARIABLE_ANCHOR))
|
||||
if (Peek() is { } nc && (nc == ESCAPE || nc == VARIABLE_ANCHOR))
|
||||
{
|
||||
esc = true;
|
||||
FlushText(tok, _pos - 1);
|
||||
|
@ -173,7 +173,7 @@ namespace SourceGit.Models
|
|||
if (Next() != VARIABLE_START)
|
||||
return null;
|
||||
int name_start = _pos;
|
||||
while (Next() is char c)
|
||||
while (Next() is { } c)
|
||||
{
|
||||
// name character, continue advancing
|
||||
if (IsNameChar(c))
|
||||
|
@ -228,7 +228,7 @@ namespace SourceGit.Models
|
|||
var sb = new StringBuilder();
|
||||
var tok = _pos;
|
||||
var esc = false;
|
||||
while (Next() is char c)
|
||||
while (Next() is { } c)
|
||||
{
|
||||
if (esc)
|
||||
{
|
||||
|
@ -277,7 +277,7 @@ namespace SourceGit.Models
|
|||
var sb = new StringBuilder();
|
||||
var tok = _pos;
|
||||
var esc = false;
|
||||
while (Next() is char c)
|
||||
while (Next() is { } c)
|
||||
{
|
||||
if (esc)
|
||||
{
|
||||
|
|
|
@ -2,27 +2,19 @@
|
|||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public class TextInlineChange
|
||||
public class TextInlineChange(int dp, int dc, int ap, int ac)
|
||||
{
|
||||
public int DeletedStart { get; set; }
|
||||
public int DeletedCount { get; set; }
|
||||
public int AddedStart { get; set; }
|
||||
public int AddedCount { get; set; }
|
||||
public int DeletedStart { get; set; } = dp;
|
||||
public int DeletedCount { get; set; } = dc;
|
||||
public int AddedStart { get; set; } = ap;
|
||||
public int AddedCount { get; set; } = ac;
|
||||
|
||||
private class Chunk
|
||||
private class Chunk(int hash, int start, int size)
|
||||
{
|
||||
public int Hash;
|
||||
public readonly int Hash = hash;
|
||||
public readonly int Start = start;
|
||||
public readonly int Size = size;
|
||||
public bool Modified;
|
||||
public int Start;
|
||||
public int Size;
|
||||
|
||||
public Chunk(int hash, int start, int size)
|
||||
{
|
||||
Hash = hash;
|
||||
Modified = false;
|
||||
Start = start;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
private enum Edit
|
||||
|
@ -43,14 +35,6 @@ namespace SourceGit.Models
|
|||
public int AddEnd;
|
||||
}
|
||||
|
||||
public TextInlineChange(int dp, int dc, int ap, int ac)
|
||||
{
|
||||
DeletedStart = dp;
|
||||
DeletedCount = dc;
|
||||
AddedStart = ap;
|
||||
AddedCount = ac;
|
||||
}
|
||||
|
||||
public static List<TextInlineChange> Compare(string oldValue, string newValue)
|
||||
{
|
||||
var hashes = new Dictionary<string, int>();
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace SourceGit.Native
|
|||
public void SetupApp(AppBuilder builder)
|
||||
{
|
||||
// Fix drop shadow issue on Windows 10
|
||||
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 22000, 0))
|
||||
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 22000))
|
||||
{
|
||||
Window.WindowStateProperty.Changed.AddClassHandler<Window>((w, _) => FixWindowFrameOnWin10(w));
|
||||
Control.LoadedEvent.AddClassHandler<Window>((w, _) => FixWindowFrameOnWin10(w));
|
||||
|
@ -385,11 +385,11 @@ namespace SourceGit.Native
|
|||
Microsoft.Win32.RegistryView.Registry64);
|
||||
|
||||
// Get default class for VisualStudio.Launcher.sln - the handler for *.sln files
|
||||
if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is Microsoft.Win32.RegistryKey launcher)
|
||||
if (localMachine.OpenSubKey(@"SOFTWARE\Classes\VisualStudio.Launcher.sln\CLSID") is { } launcher)
|
||||
{
|
||||
// Get actual path to the executable
|
||||
if (launcher.GetValue(string.Empty) is string CLSID &&
|
||||
localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is Microsoft.Win32.RegistryKey devenv &&
|
||||
localMachine.OpenSubKey(@$"SOFTWARE\Classes\CLSID\{CLSID}\LocalServer32") is { } devenv &&
|
||||
devenv.GetValue(string.Empty) is string localServer32)
|
||||
return localServer32!.Trim('\"');
|
||||
}
|
||||
|
|
|
@ -529,13 +529,7 @@
|
|||
<Setter Property="MinHeight" Value="24"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="1" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid ColumnDefinitions="*,1,Auto">
|
||||
<Button x:Name="PART_PrimaryButton"
|
||||
Grid.Column="0"
|
||||
Classes="flat primary"
|
||||
|
@ -1001,12 +995,7 @@
|
|||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid Background="Transparent">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Background="Transparent" ColumnDefinitions="Auto,*">
|
||||
<Border x:Name="Border" Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" BorderBrush="{DynamicResource Brush.Border1}" BorderThickness="1" Background="Transparent" CornerRadius="2">
|
||||
<Path x:Name="Icon" Height="12" Width="12" Data="{DynamicResource Icons.Check}" Fill="{DynamicResource Brush.Accent}" IsVisible="False" Margin="0,2,0,0"/>
|
||||
</Border>
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public ContextMenu CreateChangeContextMenu()
|
||||
{
|
||||
if (_selectedChanges == null || _selectedChanges.Count != 1)
|
||||
if (_selectedChanges is not { Count: 1 })
|
||||
return null;
|
||||
|
||||
var change = _selectedChanges[0];
|
||||
|
|
|
@ -74,9 +74,11 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public class Builder
|
||||
{
|
||||
public List<BranchTreeNode> Locals => _locals;
|
||||
public List<BranchTreeNode> Remotes => _remotes;
|
||||
public List<string> InvalidExpandedNodes => _invalidExpandedNodes;
|
||||
public List<BranchTreeNode> Locals { get; } = [];
|
||||
|
||||
public List<BranchTreeNode> Remotes { get; } = [];
|
||||
|
||||
public List<string> InvalidExpandedNodes { get; } = [];
|
||||
|
||||
public Builder(Models.BranchSortMode localSortMode, Models.BranchSortMode remoteSortMode)
|
||||
{
|
||||
|
@ -109,14 +111,14 @@ namespace SourceGit.ViewModels
|
|||
|
||||
fakeRemoteTime--;
|
||||
folders.Add(path, node);
|
||||
_remotes.Add(node);
|
||||
Remotes.Add(node);
|
||||
}
|
||||
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
if (branch.IsLocal)
|
||||
{
|
||||
MakeBranchNode(branch, _locals, folders, "refs/heads", bForceExpanded);
|
||||
MakeBranchNode(branch, Locals, folders, "refs/heads", bForceExpanded);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -131,20 +133,20 @@ namespace SourceGit.ViewModels
|
|||
foreach (var path in _expanded)
|
||||
{
|
||||
if (!folders.ContainsKey(path))
|
||||
_invalidExpandedNodes.Add(path);
|
||||
InvalidExpandedNodes.Add(path);
|
||||
}
|
||||
|
||||
folders.Clear();
|
||||
|
||||
if (_localSortMode == Models.BranchSortMode.Name)
|
||||
SortNodesByName(_locals);
|
||||
SortNodesByName(Locals);
|
||||
else
|
||||
SortNodesByTime(_locals);
|
||||
SortNodesByTime(Locals);
|
||||
|
||||
if (_remoteSortMode == Models.BranchSortMode.Name)
|
||||
SortNodesByName(_remotes);
|
||||
SortNodesByName(Remotes);
|
||||
else
|
||||
SortNodesByTime(_remotes);
|
||||
SortNodesByTime(Remotes);
|
||||
}
|
||||
|
||||
private void MakeBranchNode(Models.Branch branch, List<BranchTreeNode> roots, Dictionary<string, BranchTreeNode> folders, string prefix, bool bForceExpanded)
|
||||
|
@ -269,9 +271,6 @@ namespace SourceGit.ViewModels
|
|||
|
||||
private readonly Models.BranchSortMode _localSortMode = Models.BranchSortMode.Name;
|
||||
private readonly Models.BranchSortMode _remoteSortMode = Models.BranchSortMode.Name;
|
||||
private readonly List<BranchTreeNode> _locals = new List<BranchTreeNode>();
|
||||
private readonly List<BranchTreeNode> _remotes = new List<BranchTreeNode>();
|
||||
private readonly List<string> _invalidExpandedNodes = new List<string>();
|
||||
private readonly HashSet<string> _expanded = new HashSet<string>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
if (SetProperty(ref _selectedChanges, value))
|
||||
{
|
||||
if (value == null || value.Count != 1)
|
||||
if (value is not { Count: 1 })
|
||||
DiffContext = null;
|
||||
else
|
||||
DiffContext = new DiffContext(_repo.FullPath, new Models.DiffOption(_commit, value[0]), _diffContext);
|
||||
|
|
|
@ -46,10 +46,7 @@ namespace SourceGit.ViewModels
|
|||
else if (!string.IsNullOrEmpty(_repo.Settings.DefaultRemote))
|
||||
{
|
||||
var def = _repo.Remotes.Find(r => r.Name == _repo.Settings.DefaultRemote);
|
||||
if (def != null)
|
||||
SelectedRemote = def;
|
||||
else
|
||||
SelectedRemote = _repo.Remotes[0];
|
||||
SelectedRemote = def ?? _repo.Remotes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -240,8 +240,7 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
if (firstRemoteBranch == null)
|
||||
firstRemoteBranch = remoteBranch;
|
||||
firstRemoteBranch ??= remoteBranch;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,9 +554,7 @@ namespace SourceGit.ViewModels
|
|||
var parents = new List<Models.Commit>();
|
||||
foreach (var sha in commit.Parents)
|
||||
{
|
||||
var parent = _commits.Find(x => x.SHA == sha);
|
||||
if (parent == null)
|
||||
parent = new Commands.QuerySingleCommit(_repo.FullPath, sha).Result();
|
||||
var parent = _commits.Find(x => x.SHA == sha) ?? new Commands.QuerySingleCommit(_repo.FullPath, sha).Result();
|
||||
|
||||
if (parent != null)
|
||||
parents.Add(parent);
|
||||
|
|
|
@ -65,17 +65,14 @@ namespace SourceGit.ViewModels
|
|||
var repos = ActiveWorkspace.Repositories.ToArray();
|
||||
foreach (var repo in repos)
|
||||
{
|
||||
var node = pref.FindNode(repo);
|
||||
if (node == null)
|
||||
{
|
||||
node = new RepositoryNode()
|
||||
var node = pref.FindNode(repo) ??
|
||||
new RepositoryNode
|
||||
{
|
||||
Id = repo,
|
||||
Name = Path.GetFileName(repo),
|
||||
Bookmark = 0,
|
||||
IsRepository = true,
|
||||
};
|
||||
}
|
||||
|
||||
OpenRepositoryInTab(node, null);
|
||||
}
|
||||
|
@ -184,17 +181,14 @@ namespace SourceGit.ViewModels
|
|||
var repos = to.Repositories.ToArray();
|
||||
foreach (var repo in repos)
|
||||
{
|
||||
var node = pref.FindNode(repo);
|
||||
if (node == null)
|
||||
{
|
||||
node = new RepositoryNode()
|
||||
var node = pref.FindNode(repo) ??
|
||||
new RepositoryNode
|
||||
{
|
||||
Id = repo,
|
||||
Name = Path.GetFileName(repo),
|
||||
Bookmark = 0,
|
||||
IsRepository = true,
|
||||
};
|
||||
}
|
||||
|
||||
OpenRepositoryInTab(node, null);
|
||||
}
|
||||
|
@ -290,8 +284,7 @@ namespace SourceGit.ViewModels
|
|||
return;
|
||||
}
|
||||
|
||||
if (page == null)
|
||||
page = _activePage;
|
||||
page ??= _activePage;
|
||||
|
||||
var removeIdx = Pages.IndexOf(page);
|
||||
var activeIdx = Pages.IndexOf(_activePage);
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public bool CanCreatePopup()
|
||||
{
|
||||
return _popup == null || !_popup.InProgress;
|
||||
return _popup is not { InProgress: true };
|
||||
}
|
||||
|
||||
public void StartPopup(Popup popup)
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace SourceGit.ViewModels
|
|||
public class Pull : Popup
|
||||
{
|
||||
public List<Models.Remote> Remotes => _repo.Remotes;
|
||||
public Models.Branch Current => _current;
|
||||
public Models.Branch Current { get; }
|
||||
|
||||
public bool HasSpecifiedRemoteBranch
|
||||
{
|
||||
|
@ -64,7 +64,7 @@ namespace SourceGit.ViewModels
|
|||
public Pull(Repository repo, Models.Branch specifiedRemoteBranch)
|
||||
{
|
||||
_repo = repo;
|
||||
_current = repo.CurrentBranch;
|
||||
Current = repo.CurrentBranch;
|
||||
|
||||
if (specifiedRemoteBranch != null)
|
||||
{
|
||||
|
@ -84,12 +84,12 @@ namespace SourceGit.ViewModels
|
|||
else
|
||||
{
|
||||
var autoSelectedRemote = null as Models.Remote;
|
||||
if (!string.IsNullOrEmpty(_current.Upstream))
|
||||
if (!string.IsNullOrEmpty(Current.Upstream))
|
||||
{
|
||||
var remoteNameEndIdx = _current.Upstream.IndexOf('/', 13);
|
||||
var remoteNameEndIdx = Current.Upstream.IndexOf('/', 13);
|
||||
if (remoteNameEndIdx > 0)
|
||||
{
|
||||
var remoteName = _current.Upstream.Substring(13, remoteNameEndIdx - 13);
|
||||
var remoteName = Current.Upstream.Substring(13, remoteNameEndIdx - 13);
|
||||
autoSelectedRemote = _repo.Remotes.Find(x => x.Name == remoteName);
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ namespace SourceGit.ViewModels
|
|||
bool rs = new Commands.Pull(
|
||||
_repo.FullPath,
|
||||
_selectedRemote.Name,
|
||||
!string.IsNullOrEmpty(_current.Upstream) && _current.Upstream.Equals(_selectedBranch.FullName) ? string.Empty : _selectedBranch.Name,
|
||||
!string.IsNullOrEmpty(Current.Upstream) && Current.Upstream.Equals(_selectedBranch.FullName) ? string.Empty : _selectedBranch.Name,
|
||||
UseRebase).Use(log).Exec();
|
||||
|
||||
if (rs)
|
||||
|
@ -188,12 +188,12 @@ namespace SourceGit.ViewModels
|
|||
RemoteBranches = branches;
|
||||
|
||||
var autoSelectedBranch = false;
|
||||
if (!string.IsNullOrEmpty(_current.Upstream) &&
|
||||
_current.Upstream.StartsWith($"refs/remotes/{remoteName}/", System.StringComparison.Ordinal))
|
||||
if (!string.IsNullOrEmpty(Current.Upstream) &&
|
||||
Current.Upstream.StartsWith($"refs/remotes/{remoteName}/", System.StringComparison.Ordinal))
|
||||
{
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
if (_current.Upstream == branch.FullName)
|
||||
if (Current.Upstream == branch.FullName)
|
||||
{
|
||||
SelectedBranch = branch;
|
||||
autoSelectedBranch = true;
|
||||
|
@ -206,7 +206,7 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
foreach (var branch in branches)
|
||||
{
|
||||
if (_current.Name == branch.Name)
|
||||
if (Current.Name == branch.Name)
|
||||
{
|
||||
SelectedBranch = branch;
|
||||
autoSelectedBranch = true;
|
||||
|
@ -220,7 +220,6 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
|
||||
private readonly Repository _repo = null;
|
||||
private readonly Models.Branch _current = null;
|
||||
private Models.Remote _selectedRemote = null;
|
||||
private List<Models.Branch> _remoteBranches = null;
|
||||
private Models.Branch _selectedBranch = null;
|
||||
|
|
|
@ -1424,17 +1424,14 @@ namespace SourceGit.ViewModels
|
|||
var root = Path.GetFullPath(Path.Combine(_fullpath, submodule));
|
||||
var normalizedPath = root.Replace('\\', '/').TrimEnd('/');
|
||||
|
||||
var node = Preferences.Instance.FindNode(normalizedPath);
|
||||
if (node == null)
|
||||
{
|
||||
node = new RepositoryNode()
|
||||
var node = Preferences.Instance.FindNode(normalizedPath) ??
|
||||
new RepositoryNode
|
||||
{
|
||||
Id = normalizedPath,
|
||||
Name = Path.GetFileName(normalizedPath),
|
||||
Bookmark = selfPage.Node.Bookmark,
|
||||
IsRepository = true,
|
||||
};
|
||||
}
|
||||
|
||||
App.GetLauncher().OpenRepositoryInTab(node, null);
|
||||
}
|
||||
|
@ -1453,17 +1450,14 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public void OpenWorktree(Models.Worktree worktree)
|
||||
{
|
||||
var node = Preferences.Instance.FindNode(worktree.FullPath);
|
||||
if (node == null)
|
||||
{
|
||||
node = new RepositoryNode()
|
||||
var node = Preferences.Instance.FindNode(worktree.FullPath) ??
|
||||
new RepositoryNode
|
||||
{
|
||||
Id = worktree.FullPath,
|
||||
Name = Path.GetFileName(worktree.FullPath),
|
||||
Bookmark = 0,
|
||||
IsRepository = true,
|
||||
};
|
||||
}
|
||||
|
||||
App.GetLauncher()?.OpenRepositoryInTab(node, null);
|
||||
}
|
||||
|
|
|
@ -24,10 +24,7 @@ namespace SourceGit.ViewModels
|
|||
private set => SetProperty(ref _endPoint, value);
|
||||
}
|
||||
|
||||
public bool CanSaveAsPatch
|
||||
{
|
||||
get => _canSaveAsPatch;
|
||||
}
|
||||
public bool CanSaveAsPatch { get; }
|
||||
|
||||
public List<Models.Change> VisibleChanges
|
||||
{
|
||||
|
@ -78,7 +75,7 @@ namespace SourceGit.ViewModels
|
|||
_repo = repo;
|
||||
_startPoint = (object)startPoint ?? new Models.Null();
|
||||
_endPoint = (object)endPoint ?? new Models.Null();
|
||||
_canSaveAsPatch = startPoint != null && endPoint != null;
|
||||
CanSaveAsPatch = startPoint != null && endPoint != null;
|
||||
|
||||
Task.Run(Refresh);
|
||||
}
|
||||
|
@ -138,7 +135,7 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public ContextMenu CreateChangeContextMenu()
|
||||
{
|
||||
if (_selectedChanges == null || _selectedChanges.Count != 1)
|
||||
if (_selectedChanges is not { Count: 1 })
|
||||
return null;
|
||||
|
||||
var change = _selectedChanges[0];
|
||||
|
@ -244,7 +241,6 @@ namespace SourceGit.ViewModels
|
|||
private string _repo;
|
||||
private object _startPoint = null;
|
||||
private object _endPoint = null;
|
||||
private bool _canSaveAsPatch = false;
|
||||
private List<Models.Change> _changes = null;
|
||||
private List<Models.Change> _visibleChanges = null;
|
||||
private List<Models.Change> _selectedChanges = null;
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
public class Welcome : ObservableObject
|
||||
{
|
||||
public static Welcome Instance => _instance;
|
||||
public static Welcome Instance { get; } = new();
|
||||
|
||||
public AvaloniaList<RepositoryNode> Rows
|
||||
{
|
||||
|
@ -354,7 +354,6 @@ namespace SourceGit.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private static Welcome _instance = new Welcome();
|
||||
private string _searchFilter = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,7 @@
|
|||
Icon="/App.ico"
|
||||
Title="{DynamicResource Text.Blame}"
|
||||
MinWidth="1280" MinHeight="720">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid RowDefinitions="Auto,24,*">
|
||||
<!-- TitleBar -->
|
||||
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
|
||||
<!-- Bottom border -->
|
||||
|
|
|
@ -306,7 +306,7 @@ namespace SourceGit.Views
|
|||
return;
|
||||
|
||||
var view = TextArea.TextView;
|
||||
if (view == null || !view.VisualLinesValid)
|
||||
if (view is not { VisualLinesValid: true })
|
||||
return;
|
||||
|
||||
var color = (Color)this.FindResource("SystemAccentColor")!;
|
||||
|
|
|
@ -13,13 +13,7 @@
|
|||
Title="{DynamicResource Text.BranchCompare}"
|
||||
MinWidth="1280" MinHeight="720"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="64"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid RowDefinitions="Auto,64,*">
|
||||
<!-- TitleBar -->
|
||||
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
|
||||
<!-- Bottom border -->
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace SourceGit.Views
|
|||
public class ColorPicker : Control
|
||||
{
|
||||
public static readonly StyledProperty<uint> ValueProperty =
|
||||
AvaloniaProperty.Register<ColorPicker, uint>(nameof(Value), 0);
|
||||
AvaloniaProperty.Register<ColorPicker, uint>(nameof(Value));
|
||||
|
||||
public uint Value
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace SourceGit.Views
|
|||
public class CommandLogTime : TextBlock
|
||||
{
|
||||
public static readonly StyledProperty<ViewModels.CommandLog> LogProperty =
|
||||
AvaloniaProperty.Register<CommandLogTime, ViewModels.CommandLog>(nameof(Log), null);
|
||||
AvaloniaProperty.Register<CommandLogTime, ViewModels.CommandLog>(nameof(Log));
|
||||
|
||||
public ViewModels.CommandLog Log
|
||||
{
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace SourceGit.Views
|
|||
}
|
||||
|
||||
public static readonly StyledProperty<bool> ShowAdvancedOptionsProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextBox, bool>(nameof(ShowAdvancedOptions), false);
|
||||
AvaloniaProperty.Register<CommitMessageTextBox, bool>(nameof(ShowAdvancedOptions));
|
||||
|
||||
public static readonly StyledProperty<string> TextProperty =
|
||||
AvaloniaProperty.Register<CommitMessageTextBox, string>(nameof(Text), string.Empty);
|
||||
|
@ -182,7 +182,7 @@ namespace SourceGit.Views
|
|||
{
|
||||
var menu = vm.CreateContextMenuForCommitMessages();
|
||||
menu.Placement = PlacementMode.TopEdgeAlignedLeft;
|
||||
menu?.Open(button);
|
||||
menu.Open(button);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace SourceGit.Views
|
|||
}
|
||||
|
||||
public static readonly StyledProperty<int> DateTimeFormatProperty =
|
||||
AvaloniaProperty.Register<CommitTimeTextBlock, int>(nameof(DateTimeFormat), 0);
|
||||
AvaloniaProperty.Register<CommitTimeTextBlock, int>(nameof(DateTimeFormat));
|
||||
|
||||
public int DateTimeFormat
|
||||
{
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel Margin="0,8,0,0" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<StackPanel Margin="0,8,0,0" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Button Classes="flat" Margin="0,0,0,0" Command="{Binding UseTheirs}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Path Width="12" Height="12" Data="{StaticResource Icons.Incoming}"/>
|
||||
|
|
|
@ -13,12 +13,7 @@
|
|||
Icon="/App.ico"
|
||||
Title="{DynamicResource Text.FileHistory}"
|
||||
MinWidth="1280" MinHeight="720">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<!-- TitleBar -->
|
||||
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
|
||||
<!-- Bottom border -->
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace SourceGit.Views
|
|||
public class HistoriesLayout : Grid
|
||||
{
|
||||
public static readonly StyledProperty<bool> UseHorizontalProperty =
|
||||
AvaloniaProperty.Register<HistoriesLayout, bool>(nameof(UseHorizontal), false);
|
||||
AvaloniaProperty.Register<HistoriesLayout, bool>(nameof(UseHorizontal));
|
||||
|
||||
public bool UseHorizontal
|
||||
{
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace SourceGit.Views
|
|||
{
|
||||
if (sender is ContentPresenter presenter)
|
||||
{
|
||||
if (presenter.DataContext == null || presenter.DataContext is not ViewModels.Popup)
|
||||
if (presenter.DataContext is not ViewModels.Popup)
|
||||
{
|
||||
presenter.Content = null;
|
||||
return;
|
||||
|
|
|
@ -84,7 +84,7 @@ namespace SourceGit.Views
|
|||
return;
|
||||
|
||||
var geo = new StreamGeometry();
|
||||
var angle = Math.PI / 2;
|
||||
const double angle = Math.PI / 2;
|
||||
var y = height + 0.5;
|
||||
using (var ctx = geo.Open())
|
||||
{
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
|
||||
namespace SourceGit.Views
|
||||
{
|
||||
public class MenuItemExtension : AvaloniaObject
|
||||
{
|
||||
public static readonly AttachedProperty<string> CommandProperty =
|
||||
AvaloniaProperty.RegisterAttached<MenuItemExtension, MenuItem, string>("Command", string.Empty, false, BindingMode.OneWay);
|
||||
AvaloniaProperty.RegisterAttached<MenuItemExtension, MenuItem, string>("Command", string.Empty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace SourceGit.Views
|
|||
public class CounterPresenter : Control
|
||||
{
|
||||
public static readonly StyledProperty<int> CountProperty =
|
||||
AvaloniaProperty.Register<CounterPresenter, int>(nameof(Count), 0);
|
||||
AvaloniaProperty.Register<CounterPresenter, int>(nameof(Count));
|
||||
|
||||
public int Count
|
||||
{
|
||||
|
|
|
@ -155,7 +155,7 @@ namespace SourceGit.Views
|
|||
|
||||
private void NavigateToHead(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is ViewModels.Repository { CurrentBranch: { } } repo)
|
||||
if (DataContext is ViewModels.Repository { CurrentBranch: not null } repo)
|
||||
{
|
||||
repo.NavigateToCommit(repo.CurrentBranch.Head);
|
||||
e.Handled = true;
|
||||
|
|
|
@ -136,8 +136,7 @@ namespace SourceGit.Views
|
|||
{
|
||||
if (UseSyntaxHighlighting)
|
||||
{
|
||||
if (_textMate == null)
|
||||
_textMate = Models.TextMateHelper.CreateForEditor(this);
|
||||
_textMate ??= Models.TextMateHelper.CreateForEditor(this);
|
||||
|
||||
if (DataContext is Models.RevisionTextFile file)
|
||||
Models.TextMateHelper.SetGrammarByFileName(_textMate, file.FileName);
|
||||
|
|
|
@ -134,10 +134,7 @@ namespace SourceGit.Views
|
|||
set => SetValue(RevisionProperty, value);
|
||||
}
|
||||
|
||||
public AvaloniaList<ViewModels.RevisionFileTreeNode> Rows
|
||||
{
|
||||
get => _rows;
|
||||
}
|
||||
public AvaloniaList<ViewModels.RevisionFileTreeNode> Rows { get; } = [];
|
||||
|
||||
public RevisionFileTreeView()
|
||||
{
|
||||
|
@ -146,7 +143,7 @@ namespace SourceGit.Views
|
|||
|
||||
public void SetSearchResult(string file)
|
||||
{
|
||||
_rows.Clear();
|
||||
Rows.Clear();
|
||||
_searchResult.Clear();
|
||||
|
||||
var rows = new List<ViewModels.RevisionFileTreeNode>();
|
||||
|
@ -161,10 +158,10 @@ namespace SourceGit.Views
|
|||
return;
|
||||
|
||||
var objects = vm.GetRevisionFilesUnderFolder(file);
|
||||
if (objects == null || objects.Count != 1)
|
||||
if (objects is not { Count: 1 })
|
||||
return;
|
||||
|
||||
var routes = file.Split('/', StringSplitOptions.None);
|
||||
var routes = file.Split('/');
|
||||
if (routes.Length == 1)
|
||||
{
|
||||
_searchResult.Add(new ViewModels.RevisionFileTreeNode
|
||||
|
@ -202,7 +199,7 @@ namespace SourceGit.Views
|
|||
MakeRows(rows, _searchResult, 0);
|
||||
}
|
||||
|
||||
_rows.AddRange(rows);
|
||||
Rows.AddRange(rows);
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
|
@ -212,7 +209,7 @@ namespace SourceGit.Views
|
|||
node.IsExpanded = !node.IsExpanded;
|
||||
|
||||
var depth = node.Depth;
|
||||
var idx = _rows.IndexOf(node);
|
||||
var idx = Rows.IndexOf(node);
|
||||
if (idx == -1)
|
||||
return;
|
||||
|
||||
|
@ -223,21 +220,21 @@ namespace SourceGit.Views
|
|||
{
|
||||
var subrows = new List<ViewModels.RevisionFileTreeNode>();
|
||||
MakeRows(subrows, subtree, depth + 1);
|
||||
_rows.InsertRange(idx + 1, subrows);
|
||||
Rows.InsertRange(idx + 1, subrows);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var removeCount = 0;
|
||||
for (int i = idx + 1; i < _rows.Count; i++)
|
||||
for (int i = idx + 1; i < Rows.Count; i++)
|
||||
{
|
||||
var row = _rows[i];
|
||||
var row = Rows[i];
|
||||
if (row.Depth <= depth)
|
||||
break;
|
||||
|
||||
removeCount++;
|
||||
}
|
||||
_rows.RemoveRange(idx + 1, removeCount);
|
||||
Rows.RemoveRange(idx + 1, removeCount);
|
||||
}
|
||||
|
||||
_disableSelectionChangingEvent = false;
|
||||
|
@ -250,7 +247,7 @@ namespace SourceGit.Views
|
|||
if (change.Property == RevisionProperty)
|
||||
{
|
||||
_tree.Clear();
|
||||
_rows.Clear();
|
||||
Rows.Clear();
|
||||
_searchResult.Clear();
|
||||
|
||||
var vm = DataContext as ViewModels.CommitDetail;
|
||||
|
@ -274,7 +271,7 @@ namespace SourceGit.Views
|
|||
|
||||
var topTree = new List<ViewModels.RevisionFileTreeNode>();
|
||||
MakeRows(topTree, _tree, 0);
|
||||
_rows.AddRange(topTree);
|
||||
Rows.AddRange(topTree);
|
||||
GC.Collect();
|
||||
}
|
||||
}
|
||||
|
@ -365,7 +362,6 @@ namespace SourceGit.Views
|
|||
}
|
||||
|
||||
private List<ViewModels.RevisionFileTreeNode> _tree = [];
|
||||
private AvaloniaList<ViewModels.RevisionFileTreeNode> _rows = [];
|
||||
private bool _disableSelectionChangingEvent = false;
|
||||
private List<ViewModels.RevisionFileTreeNode> _searchResult = [];
|
||||
}
|
||||
|
|
|
@ -348,16 +348,11 @@ namespace SourceGit.Views
|
|||
private ThemedTextDiffPresenter _presenter = null;
|
||||
}
|
||||
|
||||
public class LineStyleTransformer : DocumentColorizingTransformer
|
||||
public class LineStyleTransformer(ThemedTextDiffPresenter presenter) : DocumentColorizingTransformer
|
||||
{
|
||||
public LineStyleTransformer(ThemedTextDiffPresenter presenter)
|
||||
{
|
||||
_presenter = presenter;
|
||||
}
|
||||
|
||||
protected override void ColorizeLine(DocumentLine line)
|
||||
{
|
||||
var lines = _presenter.GetLines();
|
||||
var lines = presenter.GetLines();
|
||||
var idx = line.LineNumber;
|
||||
if (idx > lines.Count)
|
||||
return;
|
||||
|
@ -367,13 +362,11 @@ namespace SourceGit.Views
|
|||
{
|
||||
ChangeLinePart(line.Offset, line.EndOffset, v =>
|
||||
{
|
||||
v.TextRunProperties.SetForegroundBrush(_presenter.IndicatorForeground);
|
||||
v.TextRunProperties.SetTypeface(new Typeface(_presenter.FontFamily, FontStyle.Italic));
|
||||
v.TextRunProperties.SetForegroundBrush(presenter.IndicatorForeground);
|
||||
v.TextRunProperties.SetTypeface(new Typeface(presenter.FontFamily, FontStyle.Italic));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ThemedTextDiffPresenter _presenter;
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string> FileNameProperty =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue