mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-24 20:57:19 -08:00
Compare commits
12 commits
c062f27081
...
9f5cbaba09
Author | SHA1 | Date | |
---|---|---|---|
|
9f5cbaba09 | ||
|
a263002c83 | ||
|
4a3ef6267f | ||
|
3196bd41a6 | ||
|
076ab95614 | ||
|
33ff191212 | ||
|
962055dd2a | ||
|
723263d099 | ||
|
ea130967fb | ||
|
15d3ad355d | ||
|
0c04cccd52 | ||
|
655d71b0bc |
17 changed files with 351 additions and 75 deletions
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
|
@ -25,6 +25,7 @@ jobs:
|
|||
with:
|
||||
version: ${{ needs.version.outputs.version }}
|
||||
publish-packages:
|
||||
needs: [package, version]
|
||||
name: Publish Packages
|
||||
uses: ./.github/workflows/publish-packages.yml
|
||||
secrets:
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
## 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.72%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-100.00%25-brightgreen)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-97.17%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-97.59%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-99.01%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-99.86%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.58%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-99.86%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-97.03%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-97.45%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-98.87%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-100.00%25-brightgreen)](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
|
||||
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
### de_DE.axaml: 99.72%
|
||||
### de_DE.axaml: 99.58%
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Missing Keys</summary>
|
||||
|
||||
- Text.CommitDetail.Files.Search
|
||||
- Text.Diff.UseBlockNavigation
|
||||
- Text.WorkingCopy.CommitToEdit
|
||||
|
||||
</details>
|
||||
|
||||
### es_ES.axaml: 100.00%
|
||||
### es_ES.axaml: 99.86%
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Missing Keys</summary>
|
||||
|
||||
|
||||
- Text.Diff.UseBlockNavigation
|
||||
|
||||
</details>
|
||||
|
||||
### fr_FR.axaml: 97.17%
|
||||
### fr_FR.axaml: 97.03%
|
||||
|
||||
|
||||
<details>
|
||||
|
@ -29,6 +30,7 @@
|
|||
- Text.CherryPick.Mainline.Tips
|
||||
- Text.CommitCM.CherryPickMultiple
|
||||
- Text.CommitDetail.Files.Search
|
||||
- Text.Diff.UseBlockNavigation
|
||||
- Text.Fetch.Force
|
||||
- Text.Preference.Appearance.FontSize
|
||||
- Text.Preference.Appearance.FontSize.Default
|
||||
|
@ -48,7 +50,7 @@
|
|||
|
||||
</details>
|
||||
|
||||
### it_IT.axaml: 97.59%
|
||||
### it_IT.axaml: 97.45%
|
||||
|
||||
|
||||
<details>
|
||||
|
@ -59,6 +61,7 @@
|
|||
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
|
||||
- Text.Configure.OpenAI.Preferred
|
||||
- Text.Configure.OpenAI.Preferred.Tip
|
||||
- Text.Diff.UseBlockNavigation
|
||||
- Text.Fetch.Force
|
||||
- Text.Preference.General.ShowChildren
|
||||
- Text.Repository.FilterCommits
|
||||
|
@ -74,7 +77,7 @@
|
|||
|
||||
</details>
|
||||
|
||||
### pt_BR.axaml: 99.01%
|
||||
### pt_BR.axaml: 98.87%
|
||||
|
||||
|
||||
<details>
|
||||
|
@ -82,6 +85,7 @@
|
|||
|
||||
- Text.CommitDetail.Files.Search
|
||||
- Text.CommitDetail.Info.Children
|
||||
- Text.Diff.UseBlockNavigation
|
||||
- Text.Fetch.Force
|
||||
- Text.Preference.General.ShowChildren
|
||||
- Text.Repository.FilterCommits
|
||||
|
@ -90,13 +94,13 @@
|
|||
|
||||
</details>
|
||||
|
||||
### ru_RU.axaml: 99.86%
|
||||
### ru_RU.axaml: 100.00%
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Missing Keys</summary>
|
||||
|
||||
- Text.CommitDetail.Files.Search
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
8.41
|
||||
8.42
|
|
@ -15,6 +15,7 @@
|
|||
<StreamGeometry x:Key="Icons.Clean">M797 829a49 49 0 1049 49 49 49 0 00-49-49zm147-114A49 49 0 10992 764a49 49 0 00-49-49zM928 861a49 49 0 1049 49A49 49 0 00928 861zm-5-586L992 205 851 64l-71 71a67 67 0 00-94 0l235 235a67 67 0 000-94zm-853 128a32 32 0 00-32 50 1291 1291 0 0075 112L288 552c20 0 25 21 8 37l-93 86a1282 1282 0 00120 114l100-32c19-6 28 15 14 34l-40 55c26 19 53 36 82 53a89 89 0 00115-20 1391 1391 0 00256-485l-188-188s-306 224-595 198z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Clone">M1280 704c0 141-115 256-256 256H288C129 960 0 831 0 672c0-126 80-232 192-272A327 327 0 01192 384c0-177 143-320 320-320 119 0 222 64 277 160C820 204 857 192 896 192c106 0 192 86 192 192 0 24-5 48-13 69C1192 477 1280 580 1280 704zm-493-128H656V352c0-18-14-32-32-32h-96c-18 0-32 14-32 32v224h-131c-29 0-43 34-23 55l211 211c12 12 33 12 45 0l211-211c20-20 6-55-23-55z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Code">M853 102H171C133 102 102 133 102 171v683C102 891 133 922 171 922h683C891 922 922 891 922 853V171C922 133 891 102 853 102zM390 600l-48 48L205 512l137-137 48 48L301 512l88 88zM465 819l-66-18L559 205l66 18L465 819zm218-171L634 600 723 512l-88-88 48-48L819 512 683 649z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.CodeBlock">M320 171A21 21 0 00299 192v213c0 49-32 85-61 107 30 22 61 58 61 107V832A21 21 0 00320 853h85v85H320A107 107 0 01213 832V619c0-11-8-26-32-42a157 157 0 00-33-17c-11-4-18-5-20-5v-85c2 0 9-1 20-5a157 157 0 0033-17c24-16 32-32 32-42V192A107 107 0 01320 85h85v85H320zm384 0h-85V85H704A107 107 0 01811 192v213c0 11 8 26 32 42 11 7 22 13 33 17 11 4 18 5 20 5v85c-2 0-9 1-20 5a157 157 0 00-33 17c-24 16-32 31-32 42V832A107 107 0 01704 939h-85v-85H704A21 21 0 00725 832V619c0-49 32-85 61-107-30-22-61-58-61-107V192A21 21 0 00704 171z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.ColorPicker">M128 854h768v86H128zM390 797c13 13 29 19 48 19s35-6 45-19l291-288c26-22 26-64 0-90L435 83l-61 61L426 192l-272 269c-22 22-22 64 0 90l237 246zm93-544 211 211-32 32H240l243-243zM707 694c0 48 38 86 86 86 48 0 86-38 86-86 0-22-10-45-26-61L794 576l-61 61c-13 13-26 35-26 58z</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.Commit">M796 471A292 292 0 00512 256a293 293 0 00-284 215H0v144h228A293 293 0 00512 832a291 291 0 00284-217H1024V471h-228M512 688A146 146 0 01366 544A145 145 0 01512 400c80 0 146 63 146 144A146 146 0 01512 688</StreamGeometry>
|
||||
<StreamGeometry x:Key="Icons.CommitMessageGenerator">M796 561a5 5 0 014 7l-39 90a5 5 0 004 7h100a5 5 0 014 8l-178 247a5 5 0 01-9-4l32-148a5 5 0 00-5-6h-89a5 5 0 01-4-7l86-191a5 5 0 014-3h88zM731 122a73 73 0 0173 73v318a54 54 0 00-8-1H731V195H244v634h408l-16 73H244a73 73 0 01-73-73V195a73 73 0 0173-73h488zm-219 366v73h-195v-73h195zm146-146v73H317v-73h341z</StreamGeometry>
|
||||
|
|
|
@ -247,6 +247,7 @@
|
|||
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Swap</x:String>
|
||||
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Syntax Highlighting</x:String>
|
||||
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Line Word Wrap</x:String>
|
||||
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Enable Block-Navigation</x:String>
|
||||
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Open in Merge Tool</x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Show All Lines</x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Decrease Number of Visible Lines</x:String>
|
||||
|
|
|
@ -124,6 +124,7 @@
|
|||
<x:String x:Key="Text.CommitDetail.Changes.Search" xml:space="preserve">Найти изменения....</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Files" xml:space="preserve">ФАЙЛЫ</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Files.LFS" xml:space="preserve">Файл ХБФ</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Files.Search" xml:space="preserve">Поиск файлов...</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Files.Submodule" xml:space="preserve">Подмодуль</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info" xml:space="preserve">ИНФОРМАЦИЯ</x:String>
|
||||
<x:String x:Key="Text.CommitDetail.Info.Author" xml:space="preserve">АВТОР</x:String>
|
||||
|
@ -250,6 +251,7 @@
|
|||
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">Обмен</x:String>
|
||||
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">Подсветка синтаксиса </x:String>
|
||||
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">Перенос слов в строке</x:String>
|
||||
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">Разрешить навигацию по блокам</x:String>
|
||||
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">Открыть в инструменте слияния </x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">Показывать все линии</x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">Уменьшить количество видимых линий</x:String>
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交换比对双方</x:String>
|
||||
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">语法高亮</x:String>
|
||||
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">自动换行</x:String>
|
||||
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">启用基于变更块的跳转</x:String>
|
||||
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">使用外部合并工具查看</x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">显示完整文件</x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">减少可见的行数</x:String>
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
<x:String x:Key="Text.Diff.SwapCommits" xml:space="preserve">交換比對雙方</x:String>
|
||||
<x:String x:Key="Text.Diff.SyntaxHighlight" xml:space="preserve">語法上色</x:String>
|
||||
<x:String x:Key="Text.Diff.ToggleWordWrap" xml:space="preserve">自動換行</x:String>
|
||||
<x:String x:Key="Text.Diff.UseBlockNavigation" xml:space="preserve">啟用基於變更區塊的導航</x:String>
|
||||
<x:String x:Key="Text.Diff.UseMerger" xml:space="preserve">使用外部合併工具檢視</x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.All" xml:space="preserve">顯示檔案的全部內容</x:String>
|
||||
<x:String x:Key="Text.Diff.VisualLines.Decr" xml:space="preserve">減少可見的行數</x:String>
|
||||
|
|
124
src/ViewModels/BlockNavigation.cs
Normal file
124
src/ViewModels/BlockNavigation.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using System.Collections.Generic;
|
||||
using Avalonia.Collections;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public class BlockNavigation : ObservableObject
|
||||
{
|
||||
public class Block
|
||||
{
|
||||
public int Start { get; set; } = 0;
|
||||
public int End { get; set; } = 0;
|
||||
|
||||
public Block(int start, int end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public bool IsInRange(int line)
|
||||
{
|
||||
return line >= Start && line <= End;
|
||||
}
|
||||
}
|
||||
|
||||
public AvaloniaList<Block> Blocks
|
||||
{
|
||||
get;
|
||||
} = [];
|
||||
|
||||
public int Current
|
||||
{
|
||||
get => _current;
|
||||
private set => SetProperty(ref _current, value);
|
||||
}
|
||||
|
||||
public string Indicator
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Blocks.Count == 0)
|
||||
return "-/-";
|
||||
|
||||
if (_current >= 0 && _current < Blocks.Count)
|
||||
return $"{_current + 1}/{Blocks.Count}";
|
||||
|
||||
return $"-/{Blocks.Count}";
|
||||
}
|
||||
}
|
||||
|
||||
public BlockNavigation(object context)
|
||||
{
|
||||
Blocks.Clear();
|
||||
Current = -1;
|
||||
|
||||
var lines = new List<Models.TextDiffLine>();
|
||||
if (context is Models.TextDiff combined)
|
||||
lines = combined.Lines;
|
||||
else if (context is TwoSideTextDiff twoSide)
|
||||
lines = twoSide.Old;
|
||||
|
||||
if (lines.Count == 0)
|
||||
return;
|
||||
|
||||
var lineIdx = 0;
|
||||
var blockStartIdx = 0;
|
||||
var isNewBlock = true;
|
||||
var blocks = new List<Block>();
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
lineIdx++;
|
||||
if (line.Type == Models.TextDiffLineType.Added ||
|
||||
line.Type == Models.TextDiffLineType.Deleted ||
|
||||
line.Type == Models.TextDiffLineType.None)
|
||||
{
|
||||
if (isNewBlock)
|
||||
{
|
||||
isNewBlock = false;
|
||||
blockStartIdx = lineIdx;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isNewBlock)
|
||||
{
|
||||
blocks.Add(new Block(blockStartIdx, lineIdx - 1));
|
||||
isNewBlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNewBlock)
|
||||
blocks.Add(new Block(blockStartIdx, lines.Count - 1));
|
||||
|
||||
Blocks.AddRange(blocks);
|
||||
}
|
||||
|
||||
public Block GetCurrentBlock()
|
||||
{
|
||||
return (_current >= 0 && _current < Blocks.Count) ? Blocks[_current] : null;
|
||||
}
|
||||
|
||||
public Block GotoNext()
|
||||
{
|
||||
if (Blocks.Count == 0)
|
||||
return null;
|
||||
|
||||
Current = (_current + 1) % Blocks.Count;
|
||||
return Blocks[_current];
|
||||
}
|
||||
|
||||
public Block GotoPrev()
|
||||
{
|
||||
if (Blocks.Count == 0)
|
||||
return null;
|
||||
|
||||
Current = _current == -1 ? Blocks.Count - 1 : (_current - 1 + Blocks.Count) % Blocks.Count;
|
||||
return Blocks[_current];
|
||||
}
|
||||
|
||||
private int _current = -1;
|
||||
}
|
||||
}
|
|
@ -121,7 +121,7 @@ namespace SourceGit.ViewModels
|
|||
if (SetProperty(ref _revisionFileSearchFilter, value))
|
||||
{
|
||||
RevisionFileSearchSuggestion.Clear();
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
if (_revisionFiles.Count == 0)
|
||||
|
@ -132,7 +132,8 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
var files = new Commands.QueryRevisionFileNames(_repo.FullPath, sha).Result();
|
||||
|
||||
Dispatcher.UIThread.Invoke(() => {
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
if (sha == Commit.SHA)
|
||||
{
|
||||
_revisionFiles.Clear();
|
||||
|
@ -795,7 +796,7 @@ namespace SourceGit.ViewModels
|
|||
var suggestion = new List<string>();
|
||||
foreach (var file in _revisionFiles)
|
||||
{
|
||||
if (file.Contains(_revisionFileSearchFilter, StringComparison.OrdinalIgnoreCase) &&
|
||||
if (file.Contains(_revisionFileSearchFilter, StringComparison.OrdinalIgnoreCase) &&
|
||||
file.Length != _revisionFileSearchFilter.Length)
|
||||
suggestion.Add(file);
|
||||
|
||||
|
|
|
@ -206,6 +206,12 @@ namespace SourceGit.ViewModels
|
|||
set => SetProperty(ref _useFullTextDiff, value);
|
||||
}
|
||||
|
||||
public bool UseBlockNavigationInDiffView
|
||||
{
|
||||
get => _useBlockNavigationInDiffView;
|
||||
set => SetProperty(ref _useBlockNavigationInDiffView, value);
|
||||
}
|
||||
|
||||
public Models.ChangeViewMode UnstagedChangeViewMode
|
||||
{
|
||||
get => _unstagedChangeViewMode;
|
||||
|
@ -614,6 +620,7 @@ namespace SourceGit.ViewModels
|
|||
private bool _enableDiffViewWordWrap = false;
|
||||
private bool _showHiddenSymbolsInDiffView = false;
|
||||
private bool _useFullTextDiff = false;
|
||||
private bool _useBlockNavigationInDiffView = false;
|
||||
|
||||
private Models.ChangeViewMode _unstagedChangeViewMode = Models.ChangeViewMode.List;
|
||||
private Models.ChangeViewMode _stagedChangeViewMode = Models.ChangeViewMode.List;
|
||||
|
|
|
@ -41,6 +41,17 @@
|
|||
ToolTip.Tip="{DynamicResource Text.Diff.Prev}">
|
||||
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
||||
</Button>
|
||||
|
||||
<Border>
|
||||
<Border.IsVisible>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="IsTextDiff"/>
|
||||
<Binding Source="{x:Static vm:Preference.Instance}" Path="UseBlockNavigationInDiffView" Mode="OneWay"/>
|
||||
</MultiBinding>
|
||||
</Border.IsVisible>
|
||||
|
||||
<TextBlock x:Name="BlockNavigationIndicator" Classes="primary" Margin="0,0,0,0" FontSize="11" Text="-/-"/>
|
||||
</Border>
|
||||
|
||||
<Button Classes="icon_button"
|
||||
Width="28"
|
||||
|
@ -50,6 +61,14 @@
|
|||
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Down}"/>
|
||||
</Button>
|
||||
|
||||
<ToggleButton Classes="line_path"
|
||||
Width="28"
|
||||
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseBlockNavigationInDiffView, Mode=TwoWay}"
|
||||
IsVisible="{Binding IsTextDiff}"
|
||||
ToolTip.Tip="{DynamicResource Text.Diff.UseBlockNavigation}">
|
||||
<Path Width="13" Height="13" Data="{StaticResource Icons.CodeBlock}" Margin="0,3,0,0"/>
|
||||
</ToggleButton>
|
||||
|
||||
<Button Classes="icon_button"
|
||||
Width="28"
|
||||
Command="{Binding IncrUnified}"
|
||||
|
@ -239,9 +258,10 @@
|
|||
|
||||
<!-- Text Diff -->
|
||||
<DataTemplate DataType="m:TextDiff">
|
||||
<v:TextDiffView
|
||||
<v:TextDiffView
|
||||
UseSideBySideDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"
|
||||
UseFullTextDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFullTextDiff, Mode=OneWay}"/>
|
||||
UseBlockNavigation="{Binding Source={x:Static vm:Preference.Instance}, Path=UseBlockNavigationInDiffView, Mode=OneWay}"
|
||||
BlockNavigationIndicator="{Binding #BlockNavigationIndicator.Text, Mode=OneWayToSource}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Empty or only EOL changes -->
|
||||
|
|
|
@ -13,27 +13,15 @@ namespace SourceGit.Views
|
|||
|
||||
private void OnGotoPrevChange(object _, RoutedEventArgs e)
|
||||
{
|
||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (textDiff == null)
|
||||
return;
|
||||
|
||||
textDiff.GotoPrevChange();
|
||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||
presenter.ForceSyncScrollOffset();
|
||||
|
||||
var textDiff = this.FindDescendantOfType<TextDiffView>();
|
||||
textDiff?.GotoPrevChange();
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnGotoNextChange(object _, RoutedEventArgs e)
|
||||
{
|
||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (textDiff == null)
|
||||
return;
|
||||
|
||||
textDiff.GotoNextChange();
|
||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||
presenter.ForceSyncScrollOffset();
|
||||
|
||||
var textDiff = this.FindDescendantOfType<TextDiffView>();
|
||||
textDiff?.GotoNextChange();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using System;
|
||||
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"
|
||||
BlockNavigation="{Binding #ThisControl.BlockNavigation, Mode=TwoWay}"/>
|
||||
|
||||
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
|
||||
|
||||
|
@ -62,7 +63,8 @@
|
|||
WordWrap="False"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"
|
||||
BlockNavigation="{Binding #ThisControl.BlockNavigation, Mode=TwoWay}"/>
|
||||
|
||||
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
|
||||
|
||||
|
@ -83,7 +85,8 @@
|
|||
WordWrap="False"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"
|
||||
BlockNavigation="{Binding #ThisControl.BlockNavigation, Mode=TwoWay}"/>
|
||||
|
||||
<Rectangle Grid.Column="3" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
@ -254,6 +255,10 @@ namespace SourceGit.Views
|
|||
if (_presenter.Document == null || !textView.VisualLinesValid)
|
||||
return;
|
||||
|
||||
var changeBlock = _presenter.BlockNavigation?.GetCurrentBlock();
|
||||
Brush changeBlockBG = new SolidColorBrush(Colors.Gray, 0.25);
|
||||
Pen changeBlockFG = new Pen(Brushes.Gray);
|
||||
|
||||
var lines = _presenter.GetLines();
|
||||
var width = textView.Bounds.Width;
|
||||
foreach (var line in textView.VisualLines)
|
||||
|
@ -266,51 +271,62 @@ namespace SourceGit.Views
|
|||
break;
|
||||
|
||||
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 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;
|
||||
var processingIdxStart = 0;
|
||||
var processingIdxEnd = 0;
|
||||
var nextHightlight = 0;
|
||||
drawingContext.DrawRectangle(bg, null, new Rect(0, startY, width, endY - startY));
|
||||
|
||||
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;
|
||||
var h = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineBottom) - textView.VerticalOffset - y;
|
||||
|
||||
while (nextHightlight < info.Highlights.Count)
|
||||
foreach (var tl in line.TextLines)
|
||||
{
|
||||
var highlight = info.Highlights[nextHightlight];
|
||||
if (highlight.Start >= processingIdxEnd)
|
||||
break;
|
||||
processingIdxEnd += tl.Length;
|
||||
|
||||
var start = line.GetVisualColumn(highlight.Start < processingIdxStart ? processingIdxStart : highlight.Start);
|
||||
var end = line.GetVisualColumn(highlight.End >= processingIdxEnd ? processingIdxEnd : highlight.End + 1);
|
||||
var y = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) - textView.VerticalOffset;
|
||||
var h = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineBottom) - textView.VerticalOffset - y;
|
||||
|
||||
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);
|
||||
while (nextHighlight < info.Highlights.Count)
|
||||
{
|
||||
var highlight = info.Highlights[nextHighlight];
|
||||
if (highlight.Start >= processingIdxEnd)
|
||||
break;
|
||||
|
||||
if (highlight.End >= processingIdxEnd)
|
||||
break;
|
||||
var start = line.GetVisualColumn(highlight.Start < processingIdxStart ? processingIdxStart : highlight.Start);
|
||||
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.Start)
|
||||
drawingContext.DrawLine(changeBlockFG, new Point(0, startY), new Point(width, startY));
|
||||
if (index == changeBlock.End)
|
||||
drawingContext.DrawLine(changeBlockFG, new Point(0, endY), new Point(width, endY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,6 +502,15 @@ namespace SourceGit.Views
|
|||
set => SetValue(DisplayRangeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<ViewModels.BlockNavigation> BlockNavigationProperty =
|
||||
AvaloniaProperty.Register<ThemedTextDiffPresenter, ViewModels.BlockNavigation>(nameof(BlockNavigation));
|
||||
|
||||
public ViewModels.BlockNavigation BlockNavigation
|
||||
{
|
||||
get => GetValue(BlockNavigationProperty);
|
||||
set => SetValue(BlockNavigationProperty, value);
|
||||
}
|
||||
|
||||
protected override Type StyleKeyOverride => typeof(TextEditor);
|
||||
|
||||
public ThemedTextDiffPresenter(TextArea area, TextDocument doc) : base(area, doc)
|
||||
|
@ -520,6 +545,19 @@ namespace SourceGit.Views
|
|||
|
||||
public void GotoPrevChange()
|
||||
{
|
||||
var blockNavigation = BlockNavigation;
|
||||
if (blockNavigation != null)
|
||||
{
|
||||
var prev = blockNavigation.GotoPrev();
|
||||
if (prev != null)
|
||||
{
|
||||
TextArea.Caret.Line = prev.Start;
|
||||
ScrollToLine(prev.Start);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var firstLineIdx = DisplayRange.StartIdx;
|
||||
if (firstLineIdx <= 1)
|
||||
return;
|
||||
|
@ -563,6 +601,19 @@ namespace SourceGit.Views
|
|||
|
||||
public void GotoNextChange()
|
||||
{
|
||||
var blockNavigation = BlockNavigation;
|
||||
if (blockNavigation != null)
|
||||
{
|
||||
var next = blockNavigation.GotoNext();
|
||||
if (next != null)
|
||||
{
|
||||
TextArea.Caret.Line = next.Start;
|
||||
ScrollToLine(next.Start);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var lines = GetLines();
|
||||
var lastLineIdx = DisplayRange.EndIdx;
|
||||
if (lastLineIdx >= lines.Count - 1)
|
||||
|
@ -665,6 +716,22 @@ namespace SourceGit.Views
|
|||
{
|
||||
InvalidateVisual();
|
||||
}
|
||||
else if (change.Property == BlockNavigationProperty)
|
||||
{
|
||||
var oldValue = change.OldValue as ViewModels.BlockNavigation;
|
||||
var newValue = change.NewValue as ViewModels.BlockNavigation;
|
||||
if (oldValue != null)
|
||||
oldValue.PropertyChanged -= OnBlockNavigationPropertyChanged;
|
||||
if (newValue != null)
|
||||
newValue.PropertyChanged += OnBlockNavigationPropertyChanged;
|
||||
|
||||
InvalidateVisual();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBlockNavigationPropertyChanged(object _1, PropertyChangedEventArgs _2)
|
||||
{
|
||||
TextArea.TextView.Redraw();
|
||||
}
|
||||
|
||||
private void OnTextViewContextRequested(object sender, ContextRequestedEventArgs e)
|
||||
|
@ -1089,6 +1156,8 @@ namespace SourceGit.Views
|
|||
|
||||
public void ForceSyncScrollOffset()
|
||||
{
|
||||
if (_scrollViewer == null)
|
||||
return;
|
||||
if (DataContext is ViewModels.TwoSideTextDiff diff)
|
||||
diff.SyncScrollOffset = _scrollViewer?.Offset ?? Vector.Zero;
|
||||
}
|
||||
|
@ -1444,15 +1513,6 @@ namespace SourceGit.Views
|
|||
set => SetValue(UseSideBySideDiffProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<bool> UseFullTextDiffProperty =
|
||||
AvaloniaProperty.Register<TextDiffView, bool>(nameof(UseFullTextDiff));
|
||||
|
||||
public bool UseFullTextDiff
|
||||
{
|
||||
get => GetValue(UseFullTextDiffProperty);
|
||||
set => SetValue(UseFullTextDiffProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<TextDiffViewChunk> SelectedChunkProperty =
|
||||
AvaloniaProperty.Register<TextDiffView, TextDiffViewChunk>(nameof(SelectedChunk));
|
||||
|
||||
|
@ -1480,6 +1540,33 @@ namespace SourceGit.Views
|
|||
set => SetValue(EnableChunkSelectionProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<bool> UseBlockNavigationProperty =
|
||||
AvaloniaProperty.Register<TextDiffView, bool>(nameof(UseBlockNavigation));
|
||||
|
||||
public bool UseBlockNavigation
|
||||
{
|
||||
get => GetValue(UseBlockNavigationProperty);
|
||||
set => SetValue(UseBlockNavigationProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<ViewModels.BlockNavigation> BlockNavigationProperty =
|
||||
AvaloniaProperty.Register<TextDiffView, ViewModels.BlockNavigation>(nameof(BlockNavigation));
|
||||
|
||||
public ViewModels.BlockNavigation BlockNavigation
|
||||
{
|
||||
get => GetValue(BlockNavigationProperty);
|
||||
set => SetValue(BlockNavigationProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string> BlockNavigationIndicatorProperty =
|
||||
AvaloniaProperty.Register<TextDiffView, string>(nameof(BlockNavigationIndicator));
|
||||
|
||||
public string BlockNavigationIndicator
|
||||
{
|
||||
get => GetValue(BlockNavigationIndicatorProperty);
|
||||
set => SetValue(BlockNavigationIndicatorProperty, value);
|
||||
}
|
||||
|
||||
static TextDiffView()
|
||||
{
|
||||
UseSideBySideDiffProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
|
||||
|
@ -1487,7 +1574,7 @@ namespace SourceGit.Views
|
|||
v.RefreshContent(v.DataContext as Models.TextDiff, false);
|
||||
});
|
||||
|
||||
UseFullTextDiffProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
|
||||
UseBlockNavigationProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
|
||||
{
|
||||
v.RefreshContent(v.DataContext as Models.TextDiff, false);
|
||||
});
|
||||
|
@ -1513,6 +1600,32 @@ namespace SourceGit.Views
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void GotoPrevChange()
|
||||
{
|
||||
var presenter = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (presenter == null)
|
||||
return;
|
||||
|
||||
presenter.GotoPrevChange();
|
||||
if (presenter is SingleSideTextDiffPresenter singleSide)
|
||||
singleSide.ForceSyncScrollOffset();
|
||||
|
||||
BlockNavigationIndicator = BlockNavigation?.Indicator ?? string.Empty;
|
||||
}
|
||||
|
||||
public void GotoNextChange()
|
||||
{
|
||||
var presenter = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (presenter == null)
|
||||
return;
|
||||
|
||||
presenter.GotoNextChange();
|
||||
if (presenter is SingleSideTextDiffPresenter singleSide)
|
||||
singleSide.ForceSyncScrollOffset();
|
||||
|
||||
BlockNavigationIndicator = BlockNavigation?.Indicator ?? string.Empty;
|
||||
}
|
||||
|
||||
protected override void OnDataContextChanged(EventArgs e)
|
||||
{
|
||||
base.OnDataContextChanged(e);
|
||||
|
@ -1551,6 +1664,17 @@ namespace SourceGit.Views
|
|||
Editor.Content = diff;
|
||||
}
|
||||
|
||||
if (UseBlockNavigation)
|
||||
{
|
||||
BlockNavigation = new ViewModels.BlockNavigation(Editor.Content);
|
||||
BlockNavigationIndicator = BlockNavigation.Indicator;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlockNavigation = null;
|
||||
BlockNavigationIndicator = "-/-";
|
||||
}
|
||||
|
||||
IsUnstagedChange = diff.Option.IsUnstaged;
|
||||
EnableChunkSelection = diff.Option.WorkingCopyChange != null;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue