Skip to content

Commit ff515da

Browse files
PR1: Implement Single-Run Progress Bar for Realtime Scans
- Add RealtimeScanProgressIndicator with animated progress 0-100% - Progress fills over ~2 seconds with 200ms updates - Automatically stops when reaching 100% - Thread-safe implementation with proper locking - Clear on scan completion Base branch: feature/devassist-integration-branch Related to: AST-109633
1 parent db53005 commit ff515da

2 files changed

Lines changed: 81 additions & 26 deletions

File tree

ast-visual-studio-extension/CxExtension/CxAssist/Realtime/Utils/RealtimeFileScanScheduler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
using ast_visual_studio_extension.CxExtension.Utils;
12
using Microsoft.VisualStudio.Threading;
23
using System;
34
using System.Collections.Concurrent;
4-
using System.Diagnostics;
55
using System.IO;
66
using System.Threading;
77
using System.Threading.Tasks;
@@ -83,7 +83,7 @@ public void Schedule(string filePath, Func<CancellationToken, Task> work)
8383
}
8484
catch (Exception ex)
8585
{
86-
Debug.WriteLine($"RealtimeFileScanScheduler: debounced work failed for {filePath}: {ex}");
86+
OutputPaneWriter.WriteError($"Realtime scan failed for {Path.GetFileName(filePath)}: {ex.Message}");
8787
}
8888
}, CancellationToken.None);
8989
}

ast-visual-studio-extension/CxExtension/CxAssist/Realtime/Utils/RealtimeScanProgressIndicator.cs

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@
88
namespace ast_visual_studio_extension.CxExtension.CxAssist.Realtime.Utils
99
{
1010
/// <summary>
11-
/// Shows Visual Studio status bar progress with animated progress bar.
12-
/// Displays: "Checkmarx is Scanning File : filename.ext" with animated bar underneath
11+
/// Shows Visual Studio status bar progress message during realtime scans.
12+
/// Displays: "Checkmarx is Scanning File : filename.ext" with single-run progress bar.
13+
/// Progress bar fills from 0-100% once per scan, then clears.
1314
/// </summary>
1415
internal static class RealtimeScanProgressIndicator
1516
{
1617
private static readonly object ProgressLock = new object();
1718
private static int _depth;
1819
private static uint _progressCookie;
1920
private static string _currentFileName = string.Empty;
21+
private static System.Timers.Timer _progressTimer;
22+
private static uint _currentProgress = 0;
2023

2124
internal static async Task PushScanAsync(string scannerName, string sourceFilePath)
2225
{
@@ -38,10 +41,11 @@ internal static async Task PushScanAsync(string scannerName, string sourceFilePa
3841

3942
try
4043
{
41-
// Show animated progress bar with label in same call
42-
// This prevents the gap between text and bar
44+
// Show progress bar that fills from 0-100% once during the scan.
45+
// Progress fills at ~200ms per 10%, completing in ~2 seconds.
4346
string label = $"Checkmarx is Scanning File : {fileName}";
44-
statusBar.Progress(ref _progressCookie, 1, label, 1, 1);
47+
_currentProgress = 0;
48+
StartProgressBar(label);
4549
}
4650
catch
4751
{
@@ -59,40 +63,33 @@ internal static async Task PopScanAsync()
5963
if (_depth > 0)
6064
_depth--;
6165

62-
var statusBar = Package.GetGlobalService(typeof(SVsStatusbar)) as IVsStatusbar;
63-
if (statusBar == null)
64-
{
65-
if (_depth == 0)
66-
{
67-
TrySetTextFallback(string.Empty);
68-
ResetProgress();
69-
}
70-
else
71-
{
72-
TrySetTextFallback($"Checkmarx is Scanning File : {_currentFileName}");
73-
}
74-
return;
75-
}
76-
7766
try
7867
{
7968
if (_depth == 0)
8069
{
81-
// Clear the progress bar when all scans complete
82-
statusBar.Progress(ref _progressCookie, 0, string.Empty, 0, 0);
70+
// Stop progress bar and clear status bar
71+
StopProgressBar();
72+
73+
var statusBar = Package.GetGlobalService(typeof(SVsStatusbar)) as IVsStatusbar;
74+
if (statusBar != null)
75+
{
76+
statusBar.Progress(ref _progressCookie, 0, string.Empty, 0, 0);
77+
}
78+
TrySetTextFallback(string.Empty);
8379
ResetProgress();
8480
}
8581
else
8682
{
87-
// Show progress for next scan
83+
// More scans pending - show current file
8884
string label = $"Checkmarx is Scanning File : {_currentFileName}";
89-
statusBar.Progress(ref _progressCookie, 1, label, 1, 1);
85+
TrySetTextFallback(label);
9086
}
9187
}
9288
catch
9389
{
9490
if (_depth == 0)
9591
{
92+
StopProgressBar();
9693
TrySetTextFallback(string.Empty);
9794
ResetProgress();
9895
}
@@ -102,12 +99,70 @@ internal static async Task PopScanAsync()
10299

103100
/// <summary>
104101
/// Resets progress state when all scans complete.
102+
/// _progressCookie must be reset to 0 so VS allocates a fresh one on the next PushScan.
103+
/// Reusing a cookie that VS has already closed causes the bar to silently disappear.
105104
/// </summary>
106105
private static void ResetProgress()
107106
{
107+
_progressCookie = 0;
108108
_currentFileName = string.Empty;
109109
}
110110

111+
/// <summary>
112+
/// Starts progress bar that fills 0-100% once during scan.
113+
/// Updates every 200ms with +10% increment = ~2 second fill time.
114+
/// </summary>
115+
private static void StartProgressBar(string label)
116+
{
117+
StopProgressBar();
118+
119+
_currentProgress = 0;
120+
_progressTimer = new System.Timers.Timer(200);
121+
_progressTimer.Elapsed += (sender, e) =>
122+
{
123+
try
124+
{
125+
lock (ProgressLock)
126+
{
127+
_currentProgress += 10;
128+
if (_currentProgress > 100)
129+
_currentProgress = 100;
130+
131+
var statusBar = Package.GetGlobalService(typeof(SVsStatusbar)) as IVsStatusbar;
132+
if (statusBar != null)
133+
{
134+
statusBar.Progress(ref _progressCookie, 1, label, _currentProgress, 100);
135+
}
136+
137+
// Stop timer once progress reaches 100%
138+
if (_currentProgress >= 100)
139+
{
140+
StopProgressBar();
141+
}
142+
}
143+
}
144+
catch
145+
{
146+
// Ignore timer errors
147+
}
148+
};
149+
_progressTimer.AutoReset = true;
150+
_progressTimer.Start();
151+
}
152+
153+
/// <summary>
154+
/// Stops the progress bar timer.
155+
/// </summary>
156+
private static void StopProgressBar()
157+
{
158+
if (_progressTimer != null)
159+
{
160+
_progressTimer.Stop();
161+
_progressTimer.Dispose();
162+
_progressTimer = null;
163+
}
164+
}
165+
111166
private static void TrySetTextFallback(string message)
112167
{
113168
try

0 commit comments

Comments
 (0)