mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-26 21:17:20 -08:00
Compare commits
33 commits
668e71b928
...
6cab51fe09
Author | SHA1 | Date | |
---|---|---|---|
|
6cab51fe09 | ||
|
1e148c032d | ||
|
13504d1831 | ||
|
8a95a17b0e | ||
|
f545ceeb70 | ||
|
8bd5bd864e | ||
|
e6e1e4e82e | ||
|
f0d8285416 | ||
|
d98765364d | ||
|
b407dd97a1 | ||
|
069dc255d1 | ||
|
d3eca59036 | ||
|
8342702103 | ||
|
22157a5c98 | ||
|
a980cc987d | ||
|
142987f73d | ||
|
408ece148e | ||
|
11a02343a0 | ||
|
aeea77078b | ||
|
760e240db7 | ||
|
636be4a7a8 | ||
|
4882ad0ad6 | ||
|
dc5bd42477 | ||
|
5597d25313 | ||
|
07cf4e6fe0 | ||
|
57e147e84c | ||
|
1a99ce54d3 | ||
|
96a9019487 | ||
|
e0c219b46d | ||
|
0007072789 | ||
|
d0dc9ac1fe | ||
|
fbb07cf75f | ||
|
875d4b5382 |
22 changed files with 524 additions and 69 deletions
|
@ -47,7 +47,7 @@
|
||||||
|
|
||||||
## Translation Status
|
## Translation Status
|
||||||
|
|
||||||
[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.29%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-98.43%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-97.86%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-99.71%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.29%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
|
[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.14%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-98.29%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-97.72%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-99.57%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.14%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
|
||||||
|
|
||||||
## How to Use
|
## How to Use
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
### de_DE.axaml: 99.29%
|
### de_DE.axaml: 99.14%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -6,13 +6,14 @@
|
||||||
|
|
||||||
- Text.CommitDetail.Info.Children
|
- Text.CommitDetail.Info.Children
|
||||||
- Text.Preference.General.ShowChildren
|
- Text.Preference.General.ShowChildren
|
||||||
|
- Text.Repository.FilterCommits
|
||||||
- Text.Repository.HistoriesOrder
|
- Text.Repository.HistoriesOrder
|
||||||
- Text.Repository.HistoriesOrder.ByDate
|
- Text.Repository.HistoriesOrder.ByDate
|
||||||
- Text.Repository.HistoriesOrder.Topo
|
- Text.Repository.HistoriesOrder.Topo
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### es_ES.axaml: 98.43%
|
### es_ES.axaml: 98.29%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
- Text.Preference.Appearance.FontSize.Default
|
- Text.Preference.Appearance.FontSize.Default
|
||||||
- Text.Preference.Appearance.FontSize.Editor
|
- Text.Preference.Appearance.FontSize.Editor
|
||||||
- Text.Preference.General.ShowChildren
|
- Text.Preference.General.ShowChildren
|
||||||
|
- Text.Repository.FilterCommits
|
||||||
- Text.Repository.FilterCommits.Default
|
- Text.Repository.FilterCommits.Default
|
||||||
- Text.Repository.FilterCommits.Exclude
|
- Text.Repository.FilterCommits.Exclude
|
||||||
- Text.Repository.FilterCommits.Include
|
- Text.Repository.FilterCommits.Include
|
||||||
|
@ -32,7 +34,7 @@
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### fr_FR.axaml: 97.86%
|
### fr_FR.axaml: 97.72%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -46,6 +48,7 @@
|
||||||
- Text.Preference.Appearance.FontSize.Editor
|
- Text.Preference.Appearance.FontSize.Editor
|
||||||
- Text.Preference.General.ShowChildren
|
- Text.Preference.General.ShowChildren
|
||||||
- Text.Repository.CustomActions
|
- Text.Repository.CustomActions
|
||||||
|
- Text.Repository.FilterCommits
|
||||||
- Text.Repository.FilterCommits.Default
|
- Text.Repository.FilterCommits.Default
|
||||||
- Text.Repository.FilterCommits.Exclude
|
- Text.Repository.FilterCommits.Exclude
|
||||||
- Text.Repository.FilterCommits.Include
|
- Text.Repository.FilterCommits.Include
|
||||||
|
@ -56,7 +59,7 @@
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### pt_BR.axaml: 99.71%
|
### pt_BR.axaml: 99.57%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -64,10 +67,11 @@
|
||||||
|
|
||||||
- Text.CommitDetail.Info.Children
|
- Text.CommitDetail.Info.Children
|
||||||
- Text.Preference.General.ShowChildren
|
- Text.Preference.General.ShowChildren
|
||||||
|
- Text.Repository.FilterCommits
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### ru_RU.axaml: 99.29%
|
### ru_RU.axaml: 99.14%
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -75,6 +79,7 @@
|
||||||
|
|
||||||
- Text.CommitDetail.Info.Children
|
- Text.CommitDetail.Info.Children
|
||||||
- Text.Preference.General.ShowChildren
|
- Text.Preference.General.ShowChildren
|
||||||
|
- Text.Repository.FilterCommits
|
||||||
- Text.Repository.HistoriesOrder
|
- Text.Repository.HistoriesOrder
|
||||||
- Text.Repository.HistoriesOrder.ByDate
|
- Text.Repository.HistoriesOrder.ByDate
|
||||||
- Text.Repository.HistoriesOrder.Topo
|
- Text.Repository.HistoriesOrder.Topo
|
||||||
|
|
|
@ -51,6 +51,9 @@ namespace SourceGit.Commands
|
||||||
_result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine);
|
_result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_result.TextDiff != null)
|
||||||
|
_result.TextDiff.ProcessChangeBlocks();
|
||||||
|
|
||||||
return _result;
|
return _result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
|
|
||||||
|
@ -59,16 +61,70 @@ namespace SourceGit.Models
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class TextDiff
|
public class TextDiffChangeBlock
|
||||||
|
{
|
||||||
|
public TextDiffChangeBlock(int startLine, int endLine)
|
||||||
|
{
|
||||||
|
StartLine = startLine;
|
||||||
|
EndLine = endLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StartLine { get; set; } = 0;
|
||||||
|
public int EndLine { get; set; } = 0;
|
||||||
|
|
||||||
|
public bool IsInRange(int line)
|
||||||
|
{
|
||||||
|
return line >= StartLine && line <= EndLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class TextDiff : ObservableObject
|
||||||
{
|
{
|
||||||
public string File { get; set; } = string.Empty;
|
public string File { get; set; } = string.Empty;
|
||||||
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
|
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
|
||||||
public Vector ScrollOffset { get; set; } = Vector.Zero;
|
public Vector ScrollOffset { get; set; } = Vector.Zero;
|
||||||
public int MaxLineNumber = 0;
|
public int MaxLineNumber = 0;
|
||||||
|
|
||||||
|
public int CurrentChangeBlockIdx
|
||||||
|
{
|
||||||
|
get => _currentChangeBlockIdx;
|
||||||
|
set => SetProperty(ref _currentChangeBlockIdx, value);
|
||||||
|
}
|
||||||
|
|
||||||
public string Repo { get; set; } = null;
|
public string Repo { get; set; } = null;
|
||||||
public DiffOption Option { get; set; } = null;
|
public DiffOption Option { get; set; } = null;
|
||||||
|
|
||||||
|
public List<TextDiffChangeBlock> ChangeBlocks { get; set; } = [];
|
||||||
|
|
||||||
|
public void ProcessChangeBlocks()
|
||||||
|
{
|
||||||
|
ChangeBlocks.Clear();
|
||||||
|
int lineIdx = 0, blockStartIdx = 0;
|
||||||
|
bool isNewBlock = true;
|
||||||
|
foreach (var line in Lines)
|
||||||
|
{
|
||||||
|
lineIdx++;
|
||||||
|
if (line.Type == Models.TextDiffLineType.Added ||
|
||||||
|
line.Type == Models.TextDiffLineType.Deleted ||
|
||||||
|
line.Type == Models.TextDiffLineType.None) // Empty
|
||||||
|
{
|
||||||
|
if (isNewBlock)
|
||||||
|
{
|
||||||
|
isNewBlock = false;
|
||||||
|
blockStartIdx = lineIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!isNewBlock)
|
||||||
|
{
|
||||||
|
ChangeBlocks.Add(new TextDiffChangeBlock(blockStartIdx, lineIdx - 1));
|
||||||
|
isNewBlock = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public TextDiffSelection MakeSelection(int startLine, int endLine, bool isCombined, bool isOldSide)
|
public TextDiffSelection MakeSelection(int startLine, int endLine, bool isCombined, bool isOldSide)
|
||||||
{
|
{
|
||||||
var rs = new TextDiffSelection();
|
var rs = new TextDiffSelection();
|
||||||
|
@ -626,6 +682,8 @@ namespace SourceGit.Models
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int _currentChangeBlockIdx = -1; // NOTE: Use -1 as "not set".
|
||||||
|
|
||||||
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
|
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
|
||||||
private static partial Regex REG_INDICATOR();
|
private static partial Regex REG_INDICATOR();
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ namespace SourceGit.Models
|
||||||
public OpenAIChatResponse Chat(string prompt, string question, CancellationToken cancellation)
|
public OpenAIChatResponse Chat(string prompt, string question, CancellationToken cancellation)
|
||||||
{
|
{
|
||||||
var chat = new OpenAIChatRequest() { Model = Model };
|
var chat = new OpenAIChatRequest() { Model = Model };
|
||||||
chat.AddMessage("system", prompt);
|
chat.AddMessage("user", prompt);
|
||||||
chat.AddMessage("user", question);
|
chat.AddMessage("user", question);
|
||||||
|
|
||||||
var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(60) };
|
var client = new HttpClient() { Timeout = TimeSpan.FromSeconds(60) };
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace SourceGit.Models
|
namespace SourceGit.Models
|
||||||
|
@ -49,7 +50,7 @@ namespace SourceGit.Models
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return url.EndsWith(".git", StringComparison.Ordinal) && Directory.Exists(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetVisitURL(out string url)
|
public bool TryGetVisitURL(out string url)
|
||||||
|
|
|
@ -13,10 +13,7 @@ namespace SourceGit.Native
|
||||||
{
|
{
|
||||||
public void SetupApp(AppBuilder builder)
|
public void SetupApp(AppBuilder builder)
|
||||||
{
|
{
|
||||||
builder.With(new X11PlatformOptions()
|
builder.With(new X11PlatformOptions());
|
||||||
{
|
|
||||||
EnableIme = true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string FindGitExecutable()
|
public string FindGitExecutable()
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
<StreamGeometry x:Key="Icons.Empty">M469 235V107h85v128h-85zm-162-94 85 85-60 60-85-85 60-60zm469 60-85 85-60-60 85-85 60 60zm-549 183A85 85 0 01302 341H722a85 85 0 0174 42l131 225A85 85 0 01939 652V832a85 85 0 01-85 85H171a85 85 0 01-85-85v-180a85 85 0 0112-43l131-225zM722 427H302l-100 171h255l10 29a59 59 0 002 5c2 4 5 9 9 14 8 9 18 17 34 17 16 0 26-7 34-17a72 72 0 0011-18l0-0 10-29h255l-100-171zM853 683H624a155 155 0 01-12 17C593 722 560 747 512 747c-48 0-81-25-99-47a155 155 0 01-12-17H171v149h683v-149z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.Empty">M469 235V107h85v128h-85zm-162-94 85 85-60 60-85-85 60-60zm469 60-85 85-60-60 85-85 60 60zm-549 183A85 85 0 01302 341H722a85 85 0 0174 42l131 225A85 85 0 01939 652V832a85 85 0 01-85 85H171a85 85 0 01-85-85v-180a85 85 0 0112-43l131-225zM722 427H302l-100 171h255l10 29a59 59 0 002 5c2 4 5 9 9 14 8 9 18 17 34 17 16 0 26-7 34-17a72 72 0 0011-18l0-0 10-29h255l-100-171zM853 683H624a155 155 0 01-12 17C593 722 560 747 512 747c-48 0-81-25-99-47a155 155 0 01-12-17H171v149h683v-149z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.Error">M576 832C576 867 547 896 512 896 477 896 448 867 448 832 448 797 477 768 512 768 547 768 576 797 576 832ZM512 256C477 256 448 285 448 320L448 640C448 675 477 704 512 704 547 704 576 675 576 640L576 320C576 285 547 256 512 256ZM1024 896C1024 967 967 1024 896 1024L128 1024C57 1024 0 967 0 896 0 875 5 855 14 837L14 837 398 69 398 69C420 28 462 0 512 0 562 0 604 28 626 69L1008 835C1018 853 1024 874 1024 896ZM960 896C960 885 957 875 952 865L952 864 951 863 569 98C557 77 536 64 512 64 488 64 466 77 455 99L452 105 92 825 93 825 71 867C66 876 64 886 64 896 64 931 93 960 128 960L896 960C931 960 960 931 960 896Z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.Error">M576 832C576 867 547 896 512 896 477 896 448 867 448 832 448 797 477 768 512 768 547 768 576 797 576 832ZM512 256C477 256 448 285 448 320L448 640C448 675 477 704 512 704 547 704 576 675 576 640L576 320C576 285 547 256 512 256ZM1024 896C1024 967 967 1024 896 1024L128 1024C57 1024 0 967 0 896 0 875 5 855 14 837L14 837 398 69 398 69C420 28 462 0 512 0 562 0 604 28 626 69L1008 835C1018 853 1024 874 1024 896ZM960 896C960 885 957 875 952 865L952 864 951 863 569 98C557 77 536 64 512 64 488 64 466 77 455 99L452 105 92 825 93 825 71 867C66 876 64 886 64 896 64 931 93 960 128 960L896 960C931 960 960 931 960 896Z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.Explore">M928 128l-416 0-32-64-352 0-64 128 896 0zM904 704l75 0 45-448-1024 0 64 640 484 0c-105-38-180-138-180-256 0-150 122-272 272-272s272 122 272 272c0 22-3 43-8 64zM1003 914l-198-175c17-29 27-63 27-99 0-106-86-192-192-192s-192 86-192 192 86 192 192 192c36 0 70-10 99-27l175 198c23 27 62 28 87 3l6-6c25-25 23-64-3-87zM640 764c-68 0-124-56-124-124s56-124 124-124 124 56 124 124-56 124-124 124z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.Explore">M928 128l-416 0-32-64-352 0-64 128 896 0zM904 704l75 0 45-448-1024 0 64 640 484 0c-105-38-180-138-180-256 0-150 122-272 272-272s272 122 272 272c0 22-3 43-8 64zM1003 914l-198-175c17-29 27-63 27-99 0-106-86-192-192-192s-192 86-192 192 86 192 192 192c36 0 70-10 99-27l175 198c23 27 62 28 87 3l6-6c25-25 23-64-3-87zM640 764c-68 0-124-56-124-124s56-124 124-124 124 56 124 124-56 124-124 124z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.Eye">M520 168C291 168 95 311 16 512c79 201 275 344 504 344 229 0 425-143 504-344-79-201-275-344-504-344zm0 573c-126 0-229-103-229-229s103-229 229-229c126 0 229 103 229 229s-103 229-229 229zm0-367c-76 0-137 62-137 137s62 137 137 137S657 588 657 512s-62-137-137-137z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.Eye">M0 512M1024 512M512 0M512 1024M520 168C291 168 95 311 16 512c79 201 275 344 504 344 229 0 425-143 504-344-79-201-275-344-504-344zm0 573c-126 0-229-103-229-229s103-229 229-229c126 0 229 103 229 229s-103 229-229 229zm0-367c-76 0-137 62-137 137s62 137 137 137S657 588 657 512s-62-137-137-137z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.EyeClose">M734 128c-33-19-74-8-93 25l-41 70c-28-6-58-9-90-9-294 0-445 298-445 298s82 149 231 236l-31 54c-19 33-8 74 25 93 33 19 74 8 93-25L759 222C778 189 767 147 734 128zM305 512c0-115 93-208 207-208 14 0 27 1 40 4l-37 64c-1 0-2 0-2 0-77 0-140 63-140 140 0 26 7 51 20 71l-37 64C324 611 305 564 305 512zM771 301 700 423c13 27 20 57 20 89 0 110-84 200-192 208l-51 89c12 1 24 2 36 2 292 0 446-298 446-298S895 388 771 301z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.EyeClose">M734 128c-33-19-74-8-93 25l-41 70c-28-6-58-9-90-9-294 0-445 298-445 298s82 149 231 236l-31 54c-19 33-8 74 25 93 33 19 74 8 93-25L759 222C778 189 767 147 734 128zM305 512c0-115 93-208 207-208 14 0 27 1 40 4l-37 64c-1 0-2 0-2 0-77 0-140 63-140 140 0 26 7 51 20 71l-37 64C324 611 305 564 305 512zM771 301 700 423c13 27 20 57 20 89 0 110-84 200-192 208l-51 89c12 1 24 2 36 2 292 0 446-298 446-298S895 388 771 301z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.FastForward">M826 498 538 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L826 526c8-7 8-21 0-28zm-320 0L218 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L506 526c4-4 6-9 6-14 0-5-2-10-6-14z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.FastForward">M826 498 538 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L826 526c8-7 8-21 0-28zm-320 0L218 250c-11-9-26-1-26 14v496c0 15 16 23 26 14L506 526c4-4 6-9 6-14 0-5-2-10-6-14z</StreamGeometry>
|
||||||
<StreamGeometry x:Key="Icons.Fetch">M1024 896v128H0V704h128v192h768V704h128v192zM576 555 811 320 896 405l-384 384-384-384L213 320 448 555V0h128v555z</StreamGeometry>
|
<StreamGeometry x:Key="Icons.Fetch">M1024 896v128H0V704h128v192h768V704h128v192zM576 555 811 320 896 405l-384 384-384-384L213 320 448 555V0h128v555z</StreamGeometry>
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
<x:String x:Key="Text.CommitCM.Revert" xml:space="preserve">Revert Commit</x:String>
|
<x:String x:Key="Text.CommitCM.Revert" xml:space="preserve">Revert Commit</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Reword" xml:space="preserve">Reword</x:String>
|
<x:String x:Key="Text.CommitCM.Reword" xml:space="preserve">Reword</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.SaveAsPatch" xml:space="preserve">Save as Patch...</x:String>
|
<x:String x:Key="Text.CommitCM.SaveAsPatch" xml:space="preserve">Save as Patch...</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.Squash" xml:space="preserve">Squash Into Parent</x:String>
|
<x:String x:Key="Text.CommitCM.Squash" xml:space="preserve">Squash into Parent</x:String>
|
||||||
<x:String x:Key="Text.CommitCM.SquashCommitsSinceThis" xml:space="preserve">Squash Child Commits to Here</x:String>
|
<x:String x:Key="Text.CommitCM.SquashCommitsSinceThis" xml:space="preserve">Squash Child Commits to Here</x:String>
|
||||||
<x:String x:Key="Text.CommitDetail.Changes" xml:space="preserve">CHANGES</x:String>
|
<x:String x:Key="Text.CommitDetail.Changes" xml:space="preserve">CHANGES</x:String>
|
||||||
<x:String x:Key="Text.CommitDetail.Changes.Search" xml:space="preserve">Search Changes...</x:String>
|
<x:String x:Key="Text.CommitDetail.Changes.Search" xml:space="preserve">Search Changes...</x:String>
|
||||||
|
@ -542,6 +542,7 @@
|
||||||
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Enable '--reflog' Option</x:String>
|
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Enable '--reflog' Option</x:String>
|
||||||
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Open in File Browser</x:String>
|
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Open in File Browser</x:String>
|
||||||
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Search Branches/Tags/Submodules</x:String>
|
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Search Branches/Tags/Submodules</x:String>
|
||||||
|
<x:String x:Key="Text.Repository.FilterCommits" xml:space="preserve">Visibility in Graph</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">Unset</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">Unset</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">Hide in commit graph</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">Hide in commit graph</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">Filter in commit graph</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">Filter in commit graph</x:String>
|
||||||
|
|
|
@ -546,6 +546,7 @@
|
||||||
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">启用 --reflog 选项</x:String>
|
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">启用 --reflog 选项</x:String>
|
||||||
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">在文件浏览器中打开</x:String>
|
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">在文件浏览器中打开</x:String>
|
||||||
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速查找分支/标签/子模块</x:String>
|
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速查找分支/标签/子模块</x:String>
|
||||||
|
<x:String x:Key="Text.Repository.FilterCommits" xml:space="preserve">设置在列表中的可见性</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">不指定</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">不指定</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">在提交列表中隐藏</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">在提交列表中隐藏</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">使用其对提交列表过滤</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">使用其对提交列表过滤</x:String>
|
||||||
|
|
|
@ -545,6 +545,7 @@
|
||||||
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">啟用 [--reflog] 選項</x:String>
|
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">啟用 [--reflog] 選項</x:String>
|
||||||
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">在檔案瀏覽器中開啟</x:String>
|
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">在檔案瀏覽器中開啟</x:String>
|
||||||
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速搜尋分支/標籤/子模組</x:String>
|
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速搜尋分支/標籤/子模組</x:String>
|
||||||
|
<x:String x:Key="Text.Repository.FilterCommits" xml:space="preserve">設定在列表中的可視性</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">不指定</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">不指定</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">在提交清單中隱藏</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">在提交清單中隱藏</x:String>
|
||||||
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">使用其來篩選提交清單</x:String>
|
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">使用其來篩選提交清單</x:String>
|
||||||
|
|
|
@ -315,12 +315,40 @@ namespace SourceGit.ViewModels
|
||||||
ev.Handled = true;
|
ev.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var patch = new MenuItem();
|
||||||
|
patch.Header = App.Text("FileCM.SaveAsPatch");
|
||||||
|
patch.Icon = App.CreateMenuIcon("Icons.Diff");
|
||||||
|
patch.Click += async (_, e) =>
|
||||||
|
{
|
||||||
|
var storageProvider = App.GetStorageProvider();
|
||||||
|
if (storageProvider == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var options = new FilePickerSaveOptions();
|
||||||
|
options.Title = App.Text("FileCM.SaveAsPatch");
|
||||||
|
options.DefaultExtension = ".patch";
|
||||||
|
options.FileTypeChoices = [new FilePickerFileType("Patch File") { Patterns = ["*.patch"] }];
|
||||||
|
|
||||||
|
var baseRevision = _commit.Parents.Count == 0 ? "4b825dc642cb6eb9a060e54bf8d69288fbee4904" : _commit.Parents[0];
|
||||||
|
var storageFile = await storageProvider.SaveFilePickerAsync(options);
|
||||||
|
if (storageFile != null)
|
||||||
|
{
|
||||||
|
var saveTo = storageFile.Path.LocalPath;
|
||||||
|
var succ = await Task.Run(() => Commands.SaveChangesAsPatch.ProcessRevisionCompareChanges(_repo.FullPath, [change], baseRevision, _commit.SHA, saveTo));
|
||||||
|
if (succ)
|
||||||
|
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
menu.Items.Add(diffWithMerger);
|
menu.Items.Add(diffWithMerger);
|
||||||
menu.Items.Add(explore);
|
menu.Items.Add(explore);
|
||||||
menu.Items.Add(new MenuItem { Header = "-" });
|
menu.Items.Add(new MenuItem { Header = "-" });
|
||||||
menu.Items.Add(history);
|
menu.Items.Add(history);
|
||||||
menu.Items.Add(blame);
|
menu.Items.Add(blame);
|
||||||
|
menu.Items.Add(patch);
|
||||||
menu.Items.Add(new MenuItem { Header = "-" });
|
menu.Items.Add(new MenuItem { Header = "-" });
|
||||||
|
|
||||||
var resetToThisRevision = new MenuItem();
|
var resetToThisRevision = new MenuItem();
|
||||||
|
|
|
@ -51,6 +51,12 @@ namespace SourceGit.ViewModels
|
||||||
private set => SetProperty(ref _unifiedLines, value);
|
private set => SetProperty(ref _unifiedLines, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ChangeBlockIndicator
|
||||||
|
{
|
||||||
|
get => _changeBlockIndicator;
|
||||||
|
private set => SetProperty(ref _changeBlockIndicator, value);
|
||||||
|
}
|
||||||
|
|
||||||
public DiffContext(string repo, Models.DiffOption option, DiffContext previous = null)
|
public DiffContext(string repo, Models.DiffOption option, DiffContext previous = null)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
|
@ -73,6 +79,54 @@ namespace SourceGit.ViewModels
|
||||||
LoadDiffContent();
|
LoadDiffContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PrevChange()
|
||||||
|
{
|
||||||
|
if (_content is Models.TextDiff textDiff)
|
||||||
|
{
|
||||||
|
if (textDiff.CurrentChangeBlockIdx > 0)
|
||||||
|
{
|
||||||
|
textDiff.CurrentChangeBlockIdx--;
|
||||||
|
}
|
||||||
|
else if (textDiff.ChangeBlocks.Count > 0)
|
||||||
|
{
|
||||||
|
// Force property value change and (re-)jump to first change block
|
||||||
|
textDiff.CurrentChangeBlockIdx = -1;
|
||||||
|
textDiff.CurrentChangeBlockIdx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RefreshChangeBlockIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NextChange()
|
||||||
|
{
|
||||||
|
if (_content is Models.TextDiff textDiff)
|
||||||
|
{
|
||||||
|
if (textDiff.CurrentChangeBlockIdx < textDiff.ChangeBlocks.Count - 1)
|
||||||
|
{
|
||||||
|
textDiff.CurrentChangeBlockIdx++;
|
||||||
|
}
|
||||||
|
else if (textDiff.ChangeBlocks.Count > 0)
|
||||||
|
{
|
||||||
|
// Force property value change and (re-)jump to last change block
|
||||||
|
textDiff.CurrentChangeBlockIdx = -1;
|
||||||
|
textDiff.CurrentChangeBlockIdx = textDiff.ChangeBlocks.Count - 1;
|
||||||
|
}
|
||||||
|
RefreshChangeBlockIndicator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshChangeBlockIndicator()
|
||||||
|
{
|
||||||
|
string curr = "-", tot = "-";
|
||||||
|
if (_content is Models.TextDiff textDiff)
|
||||||
|
{
|
||||||
|
if (textDiff.CurrentChangeBlockIdx >= 0)
|
||||||
|
curr = (textDiff.CurrentChangeBlockIdx + 1).ToString();
|
||||||
|
tot = (textDiff.ChangeBlocks.Count).ToString();
|
||||||
|
}
|
||||||
|
ChangeBlockIndicator = curr + "/" + tot;
|
||||||
|
}
|
||||||
|
|
||||||
public void ToggleFullTextDiff()
|
public void ToggleFullTextDiff()
|
||||||
{
|
{
|
||||||
Preference.Instance.UseFullTextDiff = !Preference.Instance.UseFullTextDiff;
|
Preference.Instance.UseFullTextDiff = !Preference.Instance.UseFullTextDiff;
|
||||||
|
@ -91,6 +145,12 @@ namespace SourceGit.ViewModels
|
||||||
LoadDiffContent();
|
LoadDiffContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleTwoSideDiff()
|
||||||
|
{
|
||||||
|
Preference.Instance.UseSideBySideDiff = !Preference.Instance.UseSideBySideDiff;
|
||||||
|
RefreshChangeBlockIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
public void OpenExternalMergeTool()
|
public void OpenExternalMergeTool()
|
||||||
{
|
{
|
||||||
var toolType = Preference.Instance.ExternalMergeToolType;
|
var toolType = Preference.Instance.ExternalMergeToolType;
|
||||||
|
@ -217,7 +277,9 @@ namespace SourceGit.ViewModels
|
||||||
FileModeChange = latest.FileModeChange;
|
FileModeChange = latest.FileModeChange;
|
||||||
Content = rs;
|
Content = rs;
|
||||||
IsTextDiff = rs is Models.TextDiff;
|
IsTextDiff = rs is Models.TextDiff;
|
||||||
});
|
|
||||||
|
RefreshChangeBlockIndicator();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,6 +343,7 @@ namespace SourceGit.ViewModels
|
||||||
private string _title;
|
private string _title;
|
||||||
private string _fileModeChange = string.Empty;
|
private string _fileModeChange = string.Empty;
|
||||||
private int _unifiedLines = 4;
|
private int _unifiedLines = 4;
|
||||||
|
private string _changeBlockIndicator = "-/-";
|
||||||
private bool _isTextDiff = false;
|
private bool _isTextDiff = false;
|
||||||
private bool _ignoreWhitespace = false;
|
private bool _ignoreWhitespace = false;
|
||||||
private object _content = null;
|
private object _content = null;
|
||||||
|
|
|
@ -243,6 +243,12 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
if (canCherryPick)
|
if (canCherryPick)
|
||||||
{
|
{
|
||||||
|
// Sort selected commits in order.
|
||||||
|
selected.Sort((l, r) =>
|
||||||
|
{
|
||||||
|
return _commits.IndexOf(r) - _commits.IndexOf(l);
|
||||||
|
});
|
||||||
|
|
||||||
var cherryPickMultiple = new MenuItem();
|
var cherryPickMultiple = new MenuItem();
|
||||||
cherryPickMultiple.Header = App.Text("CommitCM.CherryPickMultiple");
|
cherryPickMultiple.Header = App.Text("CommitCM.CherryPickMultiple");
|
||||||
cherryPickMultiple.Icon = App.CreateMenuIcon("Icons.CherryPick");
|
cherryPickMultiple.Icon = App.CreateMenuIcon("Icons.CherryPick");
|
||||||
|
@ -697,6 +703,109 @@ namespace SourceGit.ViewModels
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Models.FilterMode GetFilterMode(string pattern)
|
||||||
|
{
|
||||||
|
foreach (var filter in _repo.Settings.HistoriesFilters)
|
||||||
|
{
|
||||||
|
if (filter.Pattern.Equals(pattern, StringComparison.Ordinal))
|
||||||
|
return filter.Mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Models.FilterMode.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillBranchVisibilityMenu(MenuItem submenu, Models.Branch branch)
|
||||||
|
{
|
||||||
|
var visibility = new MenuItem();
|
||||||
|
visibility.Icon = App.CreateMenuIcon("Icons.Eye");
|
||||||
|
visibility.Header = App.Text("Repository.FilterCommits");
|
||||||
|
|
||||||
|
var exclude = new MenuItem();
|
||||||
|
exclude.Icon = App.CreateMenuIcon("Icons.EyeClose");
|
||||||
|
exclude.Header = App.Text("Repository.FilterCommits.Exclude");
|
||||||
|
exclude.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
_repo.SetBranchFilterMode(branch, Models.FilterMode.Excluded);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var filterMode = GetFilterMode(branch.FullName);
|
||||||
|
if (filterMode == Models.FilterMode.None)
|
||||||
|
{
|
||||||
|
var include = new MenuItem();
|
||||||
|
include.Icon = App.CreateMenuIcon("Icons.Filter");
|
||||||
|
include.Header = App.Text("Repository.FilterCommits.Include");
|
||||||
|
include.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
_repo.SetBranchFilterMode(branch, Models.FilterMode.Included);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
visibility.Items.Add(include);
|
||||||
|
visibility.Items.Add(exclude);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var unset = new MenuItem();
|
||||||
|
unset.Header = App.Text("Repository.FilterCommits.Default");
|
||||||
|
unset.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
_repo.SetBranchFilterMode(branch, Models.FilterMode.None);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
visibility.Items.Add(exclude);
|
||||||
|
visibility.Items.Add(unset);
|
||||||
|
}
|
||||||
|
|
||||||
|
submenu.Items.Add(visibility);
|
||||||
|
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillTagVisibilityMenu(MenuItem submenu, Models.Tag tag)
|
||||||
|
{
|
||||||
|
var visibility = new MenuItem();
|
||||||
|
visibility.Icon = App.CreateMenuIcon("Icons.Eye");
|
||||||
|
visibility.Header = App.Text("Repository.FilterCommits");
|
||||||
|
|
||||||
|
var exclude = new MenuItem();
|
||||||
|
exclude.Icon = App.CreateMenuIcon("Icons.EyeClose");
|
||||||
|
exclude.Header = App.Text("Repository.FilterCommits.Exclude");
|
||||||
|
exclude.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
_repo.SetTagFilterMode(tag, Models.FilterMode.Excluded);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var filterMode = GetFilterMode(tag.Name);
|
||||||
|
if (filterMode == Models.FilterMode.None)
|
||||||
|
{
|
||||||
|
var include = new MenuItem();
|
||||||
|
include.Icon = App.CreateMenuIcon("Icons.Filter");
|
||||||
|
include.Header = App.Text("Repository.FilterCommits.Include");
|
||||||
|
include.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
_repo.SetTagFilterMode(tag, Models.FilterMode.Included);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
visibility.Items.Add(include);
|
||||||
|
visibility.Items.Add(exclude);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var unset = new MenuItem();
|
||||||
|
unset.Header = App.Text("Repository.FilterCommits.Default");
|
||||||
|
unset.Click += (_, e) =>
|
||||||
|
{
|
||||||
|
_repo.SetTagFilterMode(tag, Models.FilterMode.None);
|
||||||
|
e.Handled = true;
|
||||||
|
};
|
||||||
|
visibility.Items.Add(exclude);
|
||||||
|
visibility.Items.Add(unset);
|
||||||
|
}
|
||||||
|
|
||||||
|
submenu.Items.Add(visibility);
|
||||||
|
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
}
|
||||||
|
|
||||||
private void FillCurrentBranchMenu(ContextMenu menu, Models.Branch current)
|
private void FillCurrentBranchMenu(ContextMenu menu, Models.Branch current)
|
||||||
{
|
{
|
||||||
var submenu = new MenuItem();
|
var submenu = new MenuItem();
|
||||||
|
@ -760,6 +869,8 @@ namespace SourceGit.ViewModels
|
||||||
submenu.Items.Add(new MenuItem() { Header = "-" });
|
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FillBranchVisibilityMenu(submenu, current);
|
||||||
|
|
||||||
var rename = new MenuItem();
|
var rename = new MenuItem();
|
||||||
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", current.Name);
|
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", current.Name);
|
||||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||||
|
@ -819,6 +930,8 @@ namespace SourceGit.ViewModels
|
||||||
submenu.Items.Add(new MenuItem() { Header = "-" });
|
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FillBranchVisibilityMenu(submenu, branch);
|
||||||
|
|
||||||
var rename = new MenuItem();
|
var rename = new MenuItem();
|
||||||
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name);
|
rename.Header = new Views.NameHighlightedTextBlock("BranchCM.Rename", branch.Name);
|
||||||
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
rename.Icon = App.CreateMenuIcon("Icons.Rename");
|
||||||
|
@ -876,6 +989,8 @@ namespace SourceGit.ViewModels
|
||||||
submenu.Items.Add(merge);
|
submenu.Items.Add(merge);
|
||||||
submenu.Items.Add(new MenuItem() { Header = "-" });
|
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
|
||||||
|
FillBranchVisibilityMenu(submenu, branch);
|
||||||
|
|
||||||
var delete = new MenuItem();
|
var delete = new MenuItem();
|
||||||
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name);
|
delete.Header = new Views.NameHighlightedTextBlock("BranchCM.Delete", name);
|
||||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||||
|
@ -922,6 +1037,8 @@ namespace SourceGit.ViewModels
|
||||||
submenu.Items.Add(merge);
|
submenu.Items.Add(merge);
|
||||||
submenu.Items.Add(new MenuItem() { Header = "-" });
|
submenu.Items.Add(new MenuItem() { Header = "-" });
|
||||||
|
|
||||||
|
FillTagVisibilityMenu(submenu, tag);
|
||||||
|
|
||||||
var delete = new MenuItem();
|
var delete = new MenuItem();
|
||||||
delete.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name);
|
delete.Header = new Views.NameHighlightedTextBlock("TagCM.Delete", tag.Name);
|
||||||
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
delete.Icon = App.CreateMenuIcon("Icons.Clear");
|
||||||
|
|
|
@ -707,6 +707,13 @@ namespace SourceGit.ViewModels
|
||||||
RefreshHistoriesFilters();
|
RefreshHistoriesFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetBranchFilterMode(Models.Branch branch, Models.FilterMode mode)
|
||||||
|
{
|
||||||
|
var node = FindBranchNode(branch.IsLocal ? _localBranchTrees : _remoteBranchTrees, branch.FullName);
|
||||||
|
if (node != null)
|
||||||
|
SetBranchFilterMode(node, mode);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetBranchFilterMode(BranchTreeNode node, Models.FilterMode mode)
|
public void SetBranchFilterMode(BranchTreeNode node, Models.FilterMode mode)
|
||||||
{
|
{
|
||||||
var isLocal = node.Path.StartsWith("refs/heads/", StringComparison.Ordinal);
|
var isLocal = node.Path.StartsWith("refs/heads/", StringComparison.Ordinal);
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
|
||||||
namespace SourceGit.ViewModels
|
namespace SourceGit.ViewModels
|
||||||
|
@ -28,8 +30,8 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
return Task.Run(() =>
|
return Task.Run(() =>
|
||||||
{
|
{
|
||||||
// If it is too fast, the panel will disappear very quickly, then we'll have a bad experience.
|
var watch = new Stopwatch();
|
||||||
Task.Delay(500).Wait();
|
watch.Start();
|
||||||
|
|
||||||
var rootDir = new DirectoryInfo(RootDir);
|
var rootDir = new DirectoryInfo(RootDir);
|
||||||
var founded = new List<string>();
|
var founded = new List<string>();
|
||||||
|
@ -62,6 +64,12 @@ namespace SourceGit.ViewModels
|
||||||
Welcome.Instance.Refresh();
|
Welcome.Instance.Refresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Make sure this task takes at least 0.5s to avoid that the popup panel do not disappear very quickly.
|
||||||
|
var remain = 500 - (int)watch.Elapsed.TotalMilliseconds;
|
||||||
|
watch.Stop();
|
||||||
|
if (remain > 0)
|
||||||
|
Task.Delay(remain).Wait();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -82,6 +90,13 @@ namespace SourceGit.ViewModels
|
||||||
var subdirs = dir.GetDirectories("*", opts);
|
var subdirs = dir.GetDirectories("*", opts);
|
||||||
foreach (var subdir in subdirs)
|
foreach (var subdir in subdirs)
|
||||||
{
|
{
|
||||||
|
if (subdir.Name.Equals("node_modules", StringComparison.Ordinal) ||
|
||||||
|
subdir.Name.Equals(".svn", StringComparison.Ordinal) ||
|
||||||
|
subdir.Name.Equals(".vs", StringComparison.Ordinal) ||
|
||||||
|
subdir.Name.Equals(".vscode", StringComparison.Ordinal) ||
|
||||||
|
subdir.Name.Equals(".idea", StringComparison.Ordinal))
|
||||||
|
continue;
|
||||||
|
|
||||||
SetProgressDescription($"Scanning {subdir.FullName}...");
|
SetProgressDescription($"Scanning {subdir.FullName}...");
|
||||||
|
|
||||||
var normalizedSelf = subdir.FullName.Replace("\\", "/");
|
var normalizedSelf = subdir.FullName.Replace("\\", "/");
|
||||||
|
@ -95,14 +110,14 @@ namespace SourceGit.ViewModels
|
||||||
if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut))
|
if (test.IsSuccess && !string.IsNullOrEmpty(test.StdOut))
|
||||||
{
|
{
|
||||||
var normalized = test.StdOut.Trim().Replace("\\", "/");
|
var normalized = test.StdOut.Trim().Replace("\\", "/");
|
||||||
if (!_managed.Contains(normalizedSelf))
|
if (!_managed.Contains(normalized))
|
||||||
outs.Add(normalized);
|
outs.Add(normalized);
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth < 8)
|
if (depth < 5)
|
||||||
GetUnmanagedRepositories(subdir, outs, opts, depth + 1);
|
GetUnmanagedRepositories(subdir, outs, opts, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,10 +45,43 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
FillEmptyLines();
|
FillEmptyLines();
|
||||||
|
|
||||||
|
ProcessChangeBlocks();
|
||||||
|
|
||||||
if (previous != null && previous.File == File)
|
if (previous != null && previous.File == File)
|
||||||
_syncScrollOffset = previous._syncScrollOffset;
|
_syncScrollOffset = previous._syncScrollOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Models.TextDiffChangeBlock> ChangeBlocks { get; set; } = [];
|
||||||
|
|
||||||
|
public void ProcessChangeBlocks()
|
||||||
|
{
|
||||||
|
ChangeBlocks.Clear();
|
||||||
|
int lineIdx = 0, blockStartIdx = 0;
|
||||||
|
bool isNewBlock = true;
|
||||||
|
foreach (var line in Old) // NOTE: Same block size in both Old and New lines.
|
||||||
|
{
|
||||||
|
lineIdx++;
|
||||||
|
if (line.Type == Models.TextDiffLineType.Added ||
|
||||||
|
line.Type == Models.TextDiffLineType.Deleted ||
|
||||||
|
line.Type == Models.TextDiffLineType.None) // Empty
|
||||||
|
{
|
||||||
|
if (isNewBlock)
|
||||||
|
{
|
||||||
|
isNewBlock = false;
|
||||||
|
blockStartIdx = lineIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!isNewBlock)
|
||||||
|
{
|
||||||
|
ChangeBlocks.Add(new Models.TextDiffChangeBlock(blockStartIdx, lineIdx - 1));
|
||||||
|
isNewBlock = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ConvertsToCombinedRange(Models.TextDiff combined, ref int startLine, ref int endLine, bool isOldSide)
|
public void ConvertsToCombinedRange(Models.TextDiff combined, ref int startLine, ref int endLine, bool isOldSide)
|
||||||
{
|
{
|
||||||
endLine = Math.Min(endLine, combined.Lines.Count - 1);
|
endLine = Math.Min(endLine, combined.Lines.Count - 1);
|
||||||
|
|
|
@ -136,10 +136,12 @@ namespace SourceGit.Views
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var c = await Task.Run(() => detail.GetParent(sha));
|
var c = await Task.Run(() => detail.GetParent(sha));
|
||||||
if (c != null)
|
if (c != null && ctl.IsVisible && ctl.DataContext is string newSHA && newSHA == sha)
|
||||||
{
|
{
|
||||||
ToolTip.SetTip(ctl, c);
|
ToolTip.SetTip(ctl, c);
|
||||||
ToolTip.SetIsOpen(ctl, ctl.IsPointerOver);
|
|
||||||
|
if (ctl.IsPointerOver)
|
||||||
|
ToolTip.SetIsOpen(ctl, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,13 @@
|
||||||
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<TextBlock Classes="primary"
|
||||||
|
Margin="0,0,0,0"
|
||||||
|
Text="{Binding ChangeBlockIndicator}"
|
||||||
|
FontSize="11"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
IsVisible="{Binding IsTextDiff}"/>
|
||||||
|
|
||||||
<Button Classes="icon_button"
|
<Button Classes="icon_button"
|
||||||
Width="28"
|
Width="28"
|
||||||
Click="OnGotoNextChange"
|
Click="OnGotoNextChange"
|
||||||
|
@ -124,7 +131,8 @@
|
||||||
|
|
||||||
<ToggleButton Classes="line_path"
|
<ToggleButton Classes="line_path"
|
||||||
Width="28" Height="18"
|
Width="28" Height="18"
|
||||||
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=TwoWay}"
|
Command="{Binding ToggleTwoSideDiff}"
|
||||||
|
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"
|
||||||
IsVisible="{Binding IsTextDiff}"
|
IsVisible="{Binding IsTextDiff}"
|
||||||
ToolTip.Tip="{DynamicResource Text.Diff.SideBySide}">
|
ToolTip.Tip="{DynamicResource Text.Diff.SideBySide}">
|
||||||
<Path Width="12" Height="12" Data="{StaticResource Icons.LayoutHorizontal}" Margin="0,2,0,0"/>
|
<Path Width="12" Height="12" Data="{StaticResource Icons.LayoutHorizontal}" Margin="0,2,0,0"/>
|
||||||
|
@ -241,7 +249,8 @@
|
||||||
<DataTemplate DataType="m:TextDiff">
|
<DataTemplate DataType="m:TextDiff">
|
||||||
<v:TextDiffView
|
<v:TextDiffView
|
||||||
UseSideBySideDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"
|
UseSideBySideDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"
|
||||||
UseFullTextDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFullTextDiff, Mode=OneWay}"/>
|
UseFullTextDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFullTextDiff, Mode=OneWay}"
|
||||||
|
CurrentChangeBlockIdx="{Binding CurrentChangeBlockIdx, Mode=OneWay}"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<!-- Empty or only EOL changes -->
|
<!-- Empty or only EOL changes -->
|
||||||
|
|
|
@ -11,30 +11,48 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool UseChangeBlocks { get; set; } = true;
|
||||||
|
|
||||||
private void OnGotoPrevChange(object _, RoutedEventArgs e)
|
private void OnGotoPrevChange(object _, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
if (UseChangeBlocks)
|
||||||
if (textDiff == null)
|
{
|
||||||
return;
|
if (DataContext is ViewModels.DiffContext diffCtx)
|
||||||
|
diffCtx.PrevChange();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||||
|
if (textDiff == null)
|
||||||
|
return;
|
||||||
|
|
||||||
textDiff.GotoPrevChange();
|
textDiff.GotoPrevChange();
|
||||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||||
presenter.ForceSyncScrollOffset();
|
presenter.ForceSyncScrollOffset();
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGotoNextChange(object _, RoutedEventArgs e)
|
private void OnGotoNextChange(object _, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
if (UseChangeBlocks)
|
||||||
if (textDiff == null)
|
{
|
||||||
return;
|
if (DataContext is ViewModels.DiffContext diffCtx)
|
||||||
|
diffCtx.NextChange();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||||
|
if (textDiff == null)
|
||||||
|
return;
|
||||||
|
|
||||||
textDiff.GotoNextChange();
|
textDiff.GotoNextChange();
|
||||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||||
presenter.ForceSyncScrollOffset();
|
presenter.ForceSyncScrollOffset();
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||||
|
CurrentChangeBlockIdx="{Binding #ThisControl.CurrentChangeBlockIdx}"
|
||||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@
|
||||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||||
WordWrap="False"
|
WordWrap="False"
|
||||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||||
|
CurrentChangeBlockIdx="{Binding #ThisControl.CurrentChangeBlockIdx}"
|
||||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
@ -82,6 +84,7 @@
|
||||||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||||
WordWrap="False"
|
WordWrap="False"
|
||||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||||
|
CurrentChangeBlockIdx="{Binding #ThisControl.CurrentChangeBlockIdx}"
|
||||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
@ -254,6 +255,10 @@ namespace SourceGit.Views
|
||||||
if (_presenter.Document == null || !textView.VisualLinesValid)
|
if (_presenter.Document == null || !textView.VisualLinesValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var changeBlock = _presenter.GetCurrentChangeBlock();
|
||||||
|
Brush changeBlockBG = new SolidColorBrush(Colors.Gray, 0.25);
|
||||||
|
Pen changeBlockFG = new Pen(Brushes.Gray, 1);
|
||||||
|
|
||||||
var lines = _presenter.GetLines();
|
var lines = _presenter.GetLines();
|
||||||
var width = textView.Bounds.Width;
|
var width = textView.Bounds.Width;
|
||||||
foreach (var line in textView.VisualLines)
|
foreach (var line in textView.VisualLines)
|
||||||
|
@ -266,51 +271,63 @@ namespace SourceGit.Views
|
||||||
break;
|
break;
|
||||||
|
|
||||||
var info = lines[index - 1];
|
var info = lines[index - 1];
|
||||||
var bg = GetBrushByLineType(info.Type);
|
|
||||||
if (bg == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var startY = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.LineTop) - textView.VerticalOffset;
|
var startY = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.LineTop) - textView.VerticalOffset;
|
||||||
var endY = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - textView.VerticalOffset;
|
var endY = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - textView.VerticalOffset;
|
||||||
drawingContext.DrawRectangle(bg, null, new Rect(0, startY, width, endY - startY));
|
|
||||||
|
|
||||||
if (info.Highlights.Count > 0)
|
var bg = GetBrushByLineType(info.Type);
|
||||||
|
if (bg != null)
|
||||||
{
|
{
|
||||||
var highlightBG = info.Type == Models.TextDiffLineType.Added ? _presenter.AddedHighlightBrush : _presenter.DeletedHighlightBrush;
|
if (bg != null)
|
||||||
var processingIdxStart = 0;
|
drawingContext.DrawRectangle(bg, null, new Rect(0, startY, width, endY - startY));
|
||||||
var processingIdxEnd = 0;
|
|
||||||
var nextHightlight = 0;
|
|
||||||
|
|
||||||
foreach (var tl in line.TextLines)
|
if (info.Highlights.Count > 0)
|
||||||
{
|
{
|
||||||
processingIdxEnd += tl.Length;
|
var highlightBG = info.Type == Models.TextDiffLineType.Added ? _presenter.AddedHighlightBrush : _presenter.DeletedHighlightBrush;
|
||||||
|
var processingIdxStart = 0;
|
||||||
|
var processingIdxEnd = 0;
|
||||||
|
var nextHighlight = 0;
|
||||||
|
|
||||||
var y = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) - textView.VerticalOffset;
|
foreach (var tl in line.TextLines)
|
||||||
var h = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineBottom) - textView.VerticalOffset - y;
|
|
||||||
|
|
||||||
while (nextHightlight < info.Highlights.Count)
|
|
||||||
{
|
{
|
||||||
var highlight = info.Highlights[nextHightlight];
|
processingIdxEnd += tl.Length;
|
||||||
if (highlight.Start >= processingIdxEnd)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var start = line.GetVisualColumn(highlight.Start < processingIdxStart ? processingIdxStart : highlight.Start);
|
var y = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) - textView.VerticalOffset;
|
||||||
var end = line.GetVisualColumn(highlight.End >= processingIdxEnd ? processingIdxEnd : highlight.End + 1);
|
var h = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineBottom) - textView.VerticalOffset - y;
|
||||||
|
|
||||||
var x = line.GetTextLineVisualXPosition(tl, start) - textView.HorizontalOffset;
|
while (nextHighlight < info.Highlights.Count)
|
||||||
var w = line.GetTextLineVisualXPosition(tl, end) - textView.HorizontalOffset - x;
|
{
|
||||||
var rect = new Rect(x, y, w, h);
|
var highlight = info.Highlights[nextHighlight];
|
||||||
drawingContext.DrawRectangle(highlightBG, null, rect);
|
if (highlight.Start >= processingIdxEnd)
|
||||||
|
break;
|
||||||
|
|
||||||
if (highlight.End >= processingIdxEnd)
|
var start = line.GetVisualColumn(highlight.Start < processingIdxStart ? processingIdxStart : highlight.Start);
|
||||||
break;
|
var end = line.GetVisualColumn(highlight.End >= processingIdxEnd ? processingIdxEnd : highlight.End + 1);
|
||||||
|
|
||||||
nextHightlight++;
|
var x = line.GetTextLineVisualXPosition(tl, start) - textView.HorizontalOffset;
|
||||||
|
var w = line.GetTextLineVisualXPosition(tl, end) - textView.HorizontalOffset - x;
|
||||||
|
var rect = new Rect(x, y, w, h);
|
||||||
|
drawingContext.DrawRectangle(highlightBG, null, rect);
|
||||||
|
|
||||||
|
if (highlight.End >= processingIdxEnd)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nextHighlight++;
|
||||||
|
}
|
||||||
|
|
||||||
|
processingIdxStart = processingIdxEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
processingIdxStart = processingIdxEnd;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changeBlock != null && changeBlock.IsInRange(index))
|
||||||
|
{
|
||||||
|
drawingContext.DrawRectangle(changeBlockBG, null, new Rect(0, startY, width, endY - startY));
|
||||||
|
if (index == changeBlock.StartLine)
|
||||||
|
drawingContext.DrawLine(changeBlockFG, new Point(0, startY), new Point(width, startY));
|
||||||
|
if (index == changeBlock.EndLine)
|
||||||
|
drawingContext.DrawLine(changeBlockFG, new Point(0, endY), new Point(width, endY));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,6 +503,15 @@ namespace SourceGit.Views
|
||||||
set => SetValue(DisplayRangeProperty, value);
|
set => SetValue(DisplayRangeProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<int> CurrentChangeBlockIdxProperty =
|
||||||
|
AvaloniaProperty.Register<ThemedTextDiffPresenter, int>(nameof(CurrentChangeBlockIdx));
|
||||||
|
|
||||||
|
public int CurrentChangeBlockIdx
|
||||||
|
{
|
||||||
|
get => GetValue(CurrentChangeBlockIdxProperty);
|
||||||
|
set => SetValue(CurrentChangeBlockIdxProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
protected override Type StyleKeyOverride => typeof(TextEditor);
|
protected override Type StyleKeyOverride => typeof(TextEditor);
|
||||||
|
|
||||||
public ThemedTextDiffPresenter(TextArea area, TextDocument doc) : base(area, doc)
|
public ThemedTextDiffPresenter(TextArea area, TextDocument doc) : base(area, doc)
|
||||||
|
@ -590,6 +616,27 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Models.TextDiffChangeBlock GetCurrentChangeBlock()
|
||||||
|
{
|
||||||
|
return GetChangeBlock(CurrentChangeBlockIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Models.TextDiffChangeBlock GetChangeBlock(int changeBlockIdx)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JumpToChangeBlock(int changeBlockIdx)
|
||||||
|
{
|
||||||
|
var changeBlock = GetChangeBlock(changeBlockIdx);
|
||||||
|
if (changeBlock != null)
|
||||||
|
{
|
||||||
|
TextArea.Caret.Line = changeBlock.StartLine;
|
||||||
|
//TextArea.Caret.BringCaretToView(); // NOTE: Brings caret line (barely) into view.
|
||||||
|
ScrollToLine(changeBlock.StartLine); // NOTE: Brings specified line into center of view.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void Render(DrawingContext context)
|
public override void Render(DrawingContext context)
|
||||||
{
|
{
|
||||||
base.Render(context);
|
base.Render(context);
|
||||||
|
@ -1017,6 +1064,16 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Models.TextDiffChangeBlock GetChangeBlock(int changeBlockIdx)
|
||||||
|
{
|
||||||
|
if (DataContext is Models.TextDiff diff)
|
||||||
|
{
|
||||||
|
if (changeBlockIdx >= 0 && changeBlockIdx < diff.ChangeBlocks.Count)
|
||||||
|
return diff.ChangeBlocks[changeBlockIdx];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnLoaded(RoutedEventArgs e)
|
protected override void OnLoaded(RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnLoaded(e);
|
base.OnLoaded(e);
|
||||||
|
@ -1088,6 +1145,8 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
public void ForceSyncScrollOffset()
|
public void ForceSyncScrollOffset()
|
||||||
{
|
{
|
||||||
|
if (_scrollViewer == null)
|
||||||
|
return;
|
||||||
if (DataContext is ViewModels.TwoSideTextDiff diff)
|
if (DataContext is ViewModels.TwoSideTextDiff diff)
|
||||||
diff.SyncScrollOffset = _scrollViewer?.Offset ?? Vector.Zero;
|
diff.SyncScrollOffset = _scrollViewer?.Offset ?? Vector.Zero;
|
||||||
}
|
}
|
||||||
|
@ -1233,6 +1292,16 @@ namespace SourceGit.Views
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Models.TextDiffChangeBlock GetChangeBlock(int changeBlockIdx)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.TwoSideTextDiff diff)
|
||||||
|
{
|
||||||
|
if (changeBlockIdx >= 0 && changeBlockIdx < diff.ChangeBlocks.Count)
|
||||||
|
return diff.ChangeBlocks[changeBlockIdx];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnLoaded(RoutedEventArgs e)
|
protected override void OnLoaded(RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnLoaded(e);
|
base.OnLoaded(e);
|
||||||
|
@ -1479,6 +1548,15 @@ namespace SourceGit.Views
|
||||||
set => SetValue(EnableChunkSelectionProperty, value);
|
set => SetValue(EnableChunkSelectionProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<int> CurrentChangeBlockIdxProperty =
|
||||||
|
AvaloniaProperty.Register<TextDiffView, int>(nameof(CurrentChangeBlockIdx));
|
||||||
|
|
||||||
|
public int CurrentChangeBlockIdx
|
||||||
|
{
|
||||||
|
get => GetValue(CurrentChangeBlockIdxProperty);
|
||||||
|
set => SetValue(CurrentChangeBlockIdxProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
static TextDiffView()
|
static TextDiffView()
|
||||||
{
|
{
|
||||||
UseSideBySideDiffProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
|
UseSideBySideDiffProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
|
||||||
|
@ -1505,6 +1583,19 @@ namespace SourceGit.Views
|
||||||
v.Popup.Margin = new Thickness(0, top, right, 0);
|
v.Popup.Margin = new Thickness(0, top, right, 0);
|
||||||
v.Popup.IsVisible = true;
|
v.Popup.IsVisible = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CurrentChangeBlockIdxProperty.Changed.AddClassHandler<TextDiffView>((v, e) =>
|
||||||
|
{
|
||||||
|
if ((int)e.NewValue >= 0 && v.Editor.Presenter != null)
|
||||||
|
{
|
||||||
|
foreach (var p in v.Editor.Presenter.GetVisualDescendants().OfType<ThemedTextDiffPresenter>())
|
||||||
|
{
|
||||||
|
p.JumpToChangeBlock((int)e.NewValue);
|
||||||
|
if (p is SingleSideTextDiffPresenter ssp)
|
||||||
|
ssp.ForceSyncScrollOffset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextDiffView()
|
public TextDiffView()
|
||||||
|
@ -1552,6 +1643,8 @@ namespace SourceGit.Views
|
||||||
|
|
||||||
IsUnstagedChange = diff.Option.IsUnstaged;
|
IsUnstagedChange = diff.Option.IsUnstaged;
|
||||||
EnableChunkSelection = diff.Option.WorkingCopyChange != null;
|
EnableChunkSelection = diff.Option.WorkingCopyChange != null;
|
||||||
|
|
||||||
|
diff.CurrentChangeBlockIdx = -1; // Unset current change block.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStageChunk(object _1, RoutedEventArgs _2)
|
private void OnStageChunk(object _1, RoutedEventArgs _2)
|
||||||
|
|
Loading…
Reference in a new issue