Skip to content

Commit faa6490

Browse files
committed
Feature; Add preview statistics handling.
1 parent e373084 commit faa6490

File tree

8 files changed

+175
-24
lines changed

8 files changed

+175
-24
lines changed

CodeIngest.Desktop/CodeIngest.Desktop.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,9 @@
4343
<DependentUpon>MainWindow.axaml</DependentUpon>
4444
<SubType>Code</SubType>
4545
</Compile>
46+
<Compile Update="Views\App.axaml.cs">
47+
<DependentUpon>App.axaml</DependentUpon>
48+
<SubType>Code</SubType>
49+
</Compile>
4650
</ItemGroup>
4751
</Project>

CodeIngest.Desktop/MainViewModel.cs

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//
1010
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND.
1111

12+
using System;
1213
using System.Linq;
1314
using System.Threading.Tasks;
1415
using CodeIngestLib;
@@ -23,33 +24,43 @@ namespace CodeIngest.Desktop;
2324
public class MainViewModel : ViewModelBase
2425
{
2526
private readonly IDialogService m_dialogService;
26-
private FolderTreeRoot m_root = new FolderTreeRoot(Settings.Instance.RootFolder);
27-
27+
private readonly ActionConsolidator m_folderSelectionConsolidator;
28+
private FolderTreeRoot m_root;
29+
private ProgressToken m_backgroundRefreshProgress;
2830
private bool m_isCSharp = Settings.Instance.IsCSharp;
2931
private bool m_isCppNoHeaders = Settings.Instance.IsCppNoHeaders;
3032
private bool m_isCppWithHeaders = Settings.Instance.IsCppWithHeaders;
3133
private bool m_includeMarkdown = Settings.Instance.IncludeMarkdown;
3234
private bool m_excludeImports = Settings.Instance.ExcludeImports;
3335
private bool m_useFullPaths = Settings.Instance.UseFullPaths;
3436
private bool m_excludeComments = Settings.Instance.ExcludeComments;
37+
private int? m_previewFileCount;
38+
private long? m_previewFileSize;
39+
private bool m_isGeneratingPreview;
3540

3641
public FolderTreeRoot Root
3742
{
3843
get => m_root;
3944
set
4045
{
41-
if (SetField(ref m_root, value))
42-
Settings.Instance.RootFolder = value.Root.Clone();
46+
if (!SetField(ref m_root, value) || value == null)
47+
return;
48+
Settings.Instance.RootFolder = m_root.Root.Clone();
49+
m_root.SelectionChanged += OnFolderSelectionChanged;
50+
InvalidatePreviewStats();
4351
}
4452
}
45-
53+
4654
public bool IsCSharp
4755
{
4856
get => m_isCSharp;
4957
set
5058
{
5159
if (SetField(ref m_isCSharp, value))
60+
{
5261
Settings.Instance.IsCSharp = value;
62+
InvalidatePreviewStats();
63+
}
5364
}
5465
}
5566

@@ -59,7 +70,10 @@ public bool IsCppNoHeaders
5970
set
6071
{
6172
if (SetField(ref m_isCppNoHeaders, value))
73+
{
6274
Settings.Instance.IsCppNoHeaders = value;
75+
InvalidatePreviewStats();
76+
}
6377
}
6478
}
6579

@@ -69,7 +83,10 @@ public bool IsCppWithHeaders
6983
set
7084
{
7185
if (SetField(ref m_isCppWithHeaders, value))
86+
{
7287
Settings.Instance.IsCppWithHeaders = value;
88+
InvalidatePreviewStats();
89+
}
7390
}
7491
}
7592

@@ -79,7 +96,10 @@ public bool ExcludeImports
7996
set
8097
{
8198
if (SetField(ref m_excludeImports, value))
99+
{
82100
Settings.Instance.ExcludeImports = value;
101+
InvalidatePreviewStats();
102+
}
83103
}
84104
}
85105

@@ -89,7 +109,10 @@ public bool ExcludeComments
89109
set
90110
{
91111
if (SetField(ref m_excludeComments, value))
112+
{
92113
Settings.Instance.ExcludeComments = value;
114+
InvalidatePreviewStats();
115+
}
93116
}
94117
}
95118

@@ -99,7 +122,10 @@ public bool IncludeMarkdown
99122
set
100123
{
101124
if (SetField(ref m_includeMarkdown, value))
125+
{
102126
Settings.Instance.IncludeMarkdown = value;
127+
InvalidatePreviewStats();
128+
}
103129
}
104130
}
105131

@@ -109,10 +135,31 @@ public bool UseFullPaths
109135
set
110136
{
111137
if (SetField(ref m_useFullPaths, value))
138+
{
112139
Settings.Instance.UseFullPaths = value;
140+
InvalidatePreviewStats();
141+
}
113142
}
114143
}
115144

145+
public int? PreviewFileCount
146+
{
147+
get => m_previewFileCount;
148+
set => SetField(ref m_previewFileCount, value);
149+
}
150+
151+
public long? PreviewFileSize
152+
{
153+
get => m_previewFileSize;
154+
set => SetField(ref m_previewFileSize, value);
155+
}
156+
157+
public bool IsGeneratingPreview
158+
{
159+
get => m_isGeneratingPreview;
160+
set => SetField(ref m_isGeneratingPreview, value);
161+
}
162+
116163
public async Task SelectRoot()
117164
{
118165
var rootFolder = await m_dialogService.SelectFolderAsync("Select a folder to scan for code.");
@@ -125,6 +172,9 @@ public async Task SelectRoot()
125172
public MainViewModel(IDialogService dialogService = null)
126173
{
127174
m_dialogService = dialogService ?? DialogService.Instance;
175+
176+
m_folderSelectionConsolidator = new ActionConsolidator(RefreshPredictedSize, 2.0);
177+
Root = new FolderTreeRoot(Settings.Instance.RootFolder);
128178
}
129179

130180
public async Task RunIngest()
@@ -146,6 +196,25 @@ public async Task RunIngest()
146196
if (outputFile == null)
147197
return; // User cancelled.
148198

199+
var progress = new ProgressToken(true) { IsCancelSupported = true };
200+
(int FileCount, long OutputBytes)? result;
201+
using (m_dialogService.ShowBusy("Generating code...", progress))
202+
{
203+
var options = GetIngestOptions();
204+
var ingester = new Ingester(options);
205+
result = await Task.Run(() => ingester.Run(selectedFolders, outputFile, progress));
206+
}
207+
if (!result.HasValue)
208+
{
209+
m_dialogService.ShowMessage("Failed to generate code file.", "Please check your file permissions and try again.", MaterialIconKind.Error);
210+
return;
211+
}
212+
213+
m_dialogService.ShowMessage("Code file generated successfully.", $"{result.Value.FileCount:N0} files produced {result.Value.OutputBytes.ToSize()} of output.");
214+
}
215+
216+
private IngestOptions GetIngestOptions()
217+
{
149218
var options = new IngestOptions
150219
{
151220
ExcludeImports = ExcludeImports,
@@ -168,20 +237,53 @@ public async Task RunIngest()
168237

169238
if (IncludeMarkdown)
170239
options.FilePatterns.Add("*.md");
240+
return options;
241+
}
171242

172-
var progress = new ProgressToken(true) { IsCancelSupported = true };
173-
(int FileCount, long OutputBytes)? result;
174-
using (m_dialogService.ShowBusy("Generating code...", progress))
243+
private void OnFolderSelectionChanged(object sender, EventArgs e) =>
244+
InvalidatePreviewStats();
245+
246+
private void InvalidatePreviewStats() =>
247+
m_folderSelectionConsolidator.Invoke();
248+
249+
private void RefreshPredictedSize()
250+
{
251+
m_backgroundRefreshProgress?.Cancel();
252+
253+
var selectedFolders = Root.GetSelectedItems().ToArray();
254+
if (selectedFolders.Length == 0)
175255
{
176-
var ingester = new Ingester(options);
177-
result = await Task.Run(() => ingester.Run(selectedFolders, outputFile, progress));
256+
PreviewFileCount = 0;
257+
PreviewFileSize = 0;
258+
return; // Nothing to do.
178259
}
179-
if (!result.HasValue)
260+
261+
var options = GetIngestOptions();
262+
if (options.FilePatterns.Count == 0)
180263
{
181-
m_dialogService.ShowMessage("Failed to generate code file.", "Please check your file permissions and try again.", MaterialIconKind.Error);
182-
return;
264+
PreviewFileCount = 0;
265+
PreviewFileSize = 0;
266+
return; // Nothing search.
183267
}
184268

185-
m_dialogService.ShowMessage("Code file generated successfully.", $"{result.Value.FileCount:N0} files produced {result.Value.OutputBytes.ToSize()} of output.");
269+
var ingester = new Ingester(options);
270+
m_backgroundRefreshProgress = new ProgressToken(true);
271+
272+
Task.Run(() =>
273+
{
274+
IsGeneratingPreview = true;
275+
try
276+
{
277+
var result = ingester.Run(selectedFolders, progress: m_backgroundRefreshProgress);
278+
if (!result.HasValue || m_backgroundRefreshProgress.CancelRequested)
279+
return;
280+
PreviewFileCount = result.Value.FileCount;
281+
PreviewFileSize = result.Value.OutputBytes;
282+
}
283+
finally
284+
{
285+
IsGeneratingPreview = false;
286+
}
287+
});
186288
}
187289
}

CodeIngest.Desktop/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Avalonia;
22
using System;
33
using System.Threading.Tasks;
4+
using CodeIngest.Desktop.Views;
45
using CSharp.Core;
56

67
namespace CodeIngest.Desktop;

CodeIngest.Desktop/App.axaml renamed to CodeIngest.Desktop/Views/App.axaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
xmlns:themes="clr-namespace:Material.Styles.Themes;assembly=Material.Styles"
55
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
66
xmlns:viewModels="clr-namespace:CodeIngest.Desktop"
7-
x:Class="CodeIngest.Desktop.App"
7+
xmlns:converters="clr-namespace:CSharp.Core.Converters;assembly=CSharp.Core"
8+
x:Class="CodeIngest.Desktop.Views.App"
89
RequestedThemeVariant="Default"
910
Name="Code Ingest"
1011
x:DataType="viewModels:AppViewModel">
@@ -26,6 +27,10 @@
2627
</Style>
2728
</Application.Styles>
2829

30+
<Application.Resources>
31+
<converters:BytesToUiStringConverter x:Key="BytesToUiStringConverter" />
32+
</Application.Resources>
33+
2934
<NativeMenu.Menu>
3035
<NativeMenu>
3136
<NativeMenuItem Header="About Code Ingest" Command="{CompiledBinding AboutCommand}" />

CodeIngest.Desktop/App.axaml.cs renamed to CodeIngest.Desktop/Views/App.axaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
// about your modifications. Your contributions are valued!
99
//
1010
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND.
11+
1112
using Avalonia;
1213
using Avalonia.Controls.ApplicationLifetimes;
1314
using Avalonia.Markup.Xaml;
14-
using CodeIngest.Desktop.Views;
1515
using CSharp.Core.UI;
1616

17-
namespace CodeIngest.Desktop;
17+
namespace CodeIngest.Desktop.Views;
1818

1919
public class App : Application
2020
{

CodeIngest.Desktop/Views/MainWindow.axaml

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,44 @@
3434
</Style>
3535
</Grid.Styles>
3636

37-
<Border BorderThickness="1" Margin="8" DragDrop.AllowDrop="True">
38-
<ui:FolderTree Root="{Binding Root}" Margin="8" />
39-
</Border>
37+
<Grid RowDefinitions="*,Auto">
38+
<!-- Source Tree -->
39+
<Border BorderThickness="1" Margin="8,8,8,4" DragDrop.AllowDrop="True">
40+
<ui:FolderTree Root="{Binding Root}" Margin="8" />
41+
</Border>
42+
43+
<!-- Statistics -->
44+
<Border Grid.Row="1"
45+
BorderThickness="1" Margin="8,4,8,8">
46+
<Border.Styles>
47+
<Style Selector="TextBlock">
48+
<Setter Property="Margin" Value="4"/>
49+
</Style>
50+
</Border.Styles>
51+
52+
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,*,Auto"
53+
Margin="4">
54+
<TextBlock Grid.Column="0" Grid.Row="0" Text="Source files:"/>
55+
<TextBlock Grid.Column="1" Grid.Row="0" Text="N/A"
56+
IsVisible="{Binding PreviewFileCount, Converter={x:Static ObjectConverters.IsNull}}"/>
57+
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding PreviewFileCount, StringFormat=N0}"
58+
IsVisible="{Binding PreviewFileCount, Converter={x:Static ObjectConverters.IsNotNull}}"/>
59+
60+
<TextBlock Grid.Column="0" Grid.Row="1" Text="Output size:"/>
61+
<TextBlock Grid.Column="1" Grid.Row="1" Text="N/A"
62+
IsVisible="{Binding PreviewFileSize, Converter={x:Static ObjectConverters.IsNull}}"/>
63+
<TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding PreviewFileSize, Converter={StaticResource BytesToUiStringConverter}}"
64+
IsVisible="{Binding PreviewFileSize, Converter={x:Static ObjectConverters.IsNotNull}}"/>
65+
66+
<!-- ReSharper disable once Xaml.StyleClassNotFound -->
67+
<ProgressBar Grid.Column="2" Grid.Row="0" Grid.RowSpan="2"
68+
Classes="circular" IsIndeterminate="True"
69+
VerticalAlignment="Center"
70+
Width="36" Height="36" Margin="8,0"
71+
IsVisible="{Binding IsGeneratingPreview}"/>
72+
</Grid>
73+
</Border>
74+
</Grid>
4075

4176
<Grid Grid.Column="1" Margin="0,8,8,8"
4277
RowDefinitions="Auto,Auto,*">

CodeIngestLib/Ingester.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public Ingester(IngestOptions options)
3131
m_options = options;
3232
}
3333

34-
public (int FileCount, long OutputBytes)? Run(IEnumerable<DirectoryInfo> directories, FileInfo outputFile, ProgressToken progress = null)
34+
public (int FileCount, long OutputBytes)? Run(IEnumerable<DirectoryInfo> directories, FileInfo outputFile = null, ProgressToken progress = null)
3535
{
3636
var didError = false;
3737
var sourceFiles = directories
@@ -65,8 +65,12 @@ public Ingester(IngestOptions options)
6565
return (0, 0);
6666
}
6767

68-
using (var fileStream = outputFile.Open(FileMode.Create))
69-
using (var writer = new StreamWriter(fileStream, Encoding.UTF8))
68+
// If caller isn't collecting output (Just wants size info), write output to a temp file.
69+
using var tempOutputFile = new TempFile();
70+
outputFile ??= tempOutputFile;
71+
72+
using (var outputStream = (outputFile).Open(FileMode.Create))
73+
using (var writer = new StreamWriter(outputStream, Encoding.UTF8))
7074
{
7175
writer.NewLine = "\n";
7276
writer.WriteLine("// CodeIngest - A CLI tool that merges and processes code files for GPT reviews.");

DTC.Core

0 commit comments

Comments
 (0)