Skip to content

Commit bb36aac

Browse files
committed
updated with new utility to display progress bar
1 parent 60ec3bf commit bb36aac

File tree

1 file changed

+336
-0
lines changed

1 file changed

+336
-0
lines changed

content/docs/aesh/progress-bar.md

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
---
2+
date: '2026-02-11T15:00:00+01:00'
3+
draft: false
4+
title: 'Progress Bar'
5+
weight: 20
6+
---
7+
8+
The progress bar utility displays progress feedback for long-running operations in the terminal. It supports multiple visual styles, optional labels, percentage and ratio display, and renders in-place using ANSI escape sequences.
9+
10+
## Quick Start
11+
12+
```java
13+
import org.aesh.util.progress.ProgressBar;
14+
import org.aesh.util.progress.ProgressBarStyle;
15+
16+
ProgressBar progress = ProgressBar.builder()
17+
.shell(invocation.getShell())
18+
.total(files.size())
19+
.label("Processing")
20+
.style(ProgressBarStyle.UNICODE)
21+
.build();
22+
23+
for (File file : files) {
24+
processFile(file);
25+
progress.step();
26+
}
27+
progress.complete();
28+
```
29+
30+
Output (mid-progress):
31+
32+
```
33+
Processing │████████░░░░░░░░│ 52%
34+
```
35+
36+
## Builder API
37+
38+
All configuration is done through the builder. Only `total` is required; everything else has sensible defaults.
39+
40+
```java
41+
ProgressBar progress = ProgressBar.builder()
42+
.shell(invocation.getShell()) // Shell to write to
43+
.total(200) // total number of steps
44+
.label("Downloading") // optional text before the bar
45+
.style(ProgressBarStyle.ASCII) // visual style (default: ASCII)
46+
.showPercentage(true) // show XX% (default: true)
47+
.showRatio(true) // show current/total (default: false)
48+
.width(80) // explicit width (default: auto from terminal)
49+
.build();
50+
```
51+
52+
### Builder Options
53+
54+
| Method | Default | Description |
55+
|--------|---------|-------------|
56+
| `shell(Shell)` | `null` | The `Shell` instance for terminal output |
57+
| `total(long)` | `0` | Total number of steps (supports large values via `long`) |
58+
| `label(String)` | none | Text label displayed before the bar |
59+
| `style(ProgressBarStyle)` | `ASCII` | Visual style for bar characters |
60+
| `showPercentage(boolean)` | `true` | Whether to display the percentage |
61+
| `showRatio(boolean)` | `false` | Whether to display `current/total` |
62+
| `width(int)` | auto | Explicit terminal width; if not set, reads from `Shell.size()` or defaults to 80 |
63+
64+
## Update Methods
65+
66+
### `step()`
67+
68+
Increment progress by 1 and re-render the bar:
69+
70+
```java
71+
for (File file : files) {
72+
processFile(file);
73+
progress.step();
74+
}
75+
```
76+
77+
### `step(long n)`
78+
79+
Increment progress by a given amount:
80+
81+
```java
82+
progress.step(10); // advance by 10 steps
83+
```
84+
85+
### `update(long value)`
86+
87+
Set an absolute progress value:
88+
89+
```java
90+
progress.update(75); // jump to 75 out of total
91+
```
92+
93+
### `complete()`
94+
95+
Mark the operation as finished. This fills the bar to 100% and prints a newline so subsequent output appears below:
96+
97+
```java
98+
progress.complete();
99+
```
100+
101+
### `complete(String message)`
102+
103+
Mark complete and replace the bar line with a custom message:
104+
105+
```java
106+
progress.complete("Done! Processed 200 files.");
107+
```
108+
109+
## Progress Bar Styles
110+
111+
Four predefined styles are available via `ProgressBarStyle`:
112+
113+
### ASCII
114+
115+
The default style using standard ASCII characters:
116+
117+
```java
118+
ProgressBar.builder().style(ProgressBarStyle.ASCII)...
119+
```
120+
121+
```
122+
[####------] 40%
123+
```
124+
125+
### UNICODE
126+
127+
Unicode block characters for a solid, modern look:
128+
129+
```java
130+
ProgressBar.builder().style(ProgressBarStyle.UNICODE)...
131+
```
132+
133+
```
134+
│████░░░░░░│ 40%
135+
```
136+
137+
### SIMPLE
138+
139+
Minimal style with equals signs and spaces:
140+
141+
```java
142+
ProgressBar.builder().style(ProgressBarStyle.SIMPLE)...
143+
```
144+
145+
```
146+
[==== ] 40%
147+
```
148+
149+
### ARROW
150+
151+
Like SIMPLE but with a `>` tip character at the progress front:
152+
153+
```java
154+
ProgressBar.builder().style(ProgressBarStyle.ARROW)...
155+
```
156+
157+
```
158+
[===> ] 40%
159+
```
160+
161+
At 100% completion, the arrow tip disappears and the bar is fully filled with `=`.
162+
163+
### Style Characters
164+
165+
Each style defines five characters:
166+
167+
| Style | Fill | Empty | Left Bracket | Right Bracket | Tip |
168+
|-------|------|-------|--------------|---------------|-----|
169+
| `ASCII` | `#` | `-` | `[` | `]` | `#` |
170+
| `UNICODE` | `` | `` | `` | `` | `` |
171+
| `SIMPLE` | `=` | ` ` | `[` | `]` | `=` |
172+
| `ARROW` | `=` | ` ` | `[` | `]` | `>` |
173+
174+
## Display Options
175+
176+
### Label
177+
178+
An optional text label is rendered before the bar:
179+
180+
```java
181+
ProgressBar.builder()
182+
.label("Downloading")
183+
.total(100)
184+
.build();
185+
```
186+
187+
```
188+
Downloading [####------] 40%
189+
```
190+
191+
Without a label, the bar starts immediately:
192+
193+
```
194+
[####------] 40%
195+
```
196+
197+
### Percentage
198+
199+
Enabled by default. Disable with `showPercentage(false)`:
200+
201+
```java
202+
ProgressBar.builder()
203+
.showPercentage(false)
204+
.total(100)
205+
.build();
206+
```
207+
208+
```
209+
[####------]
210+
```
211+
212+
### Ratio
213+
214+
Disabled by default. Enable with `showRatio(true)` to show `current/total`:
215+
216+
```java
217+
ProgressBar.builder()
218+
.showRatio(true)
219+
.total(200)
220+
.build();
221+
```
222+
223+
```
224+
[####------] 40% (80/200)
225+
```
226+
227+
## Bar Width Calculation
228+
229+
The bar automatically sizes itself to fill the available terminal width. The layout is:
230+
231+
```
232+
[label ] [left-bracket] [===bar===] [right-bracket] [ XX%] [ (current/total)]
233+
```
234+
235+
The bar width is calculated as:
236+
237+
```
238+
barWidth = terminalWidth - labelLength - brackets(2) - percentageLength - ratioLength
239+
```
240+
241+
If the calculated bar width falls below 10 characters, it is clamped to a minimum of 10.
242+
243+
When no explicit width is set via `width()`, the builder reads the terminal width from `Shell.size().getWidth()`. If the shell is unavailable, it defaults to 80 columns.
244+
245+
## Using in Commands
246+
247+
Inside a command's `execute()` method, pass the `Shell` from the `CommandInvocation` to the builder:
248+
249+
```java
250+
import org.aesh.command.Command;
251+
import org.aesh.command.CommandDefinition;
252+
import org.aesh.command.CommandResult;
253+
import org.aesh.command.invocation.CommandInvocation;
254+
import org.aesh.util.progress.ProgressBar;
255+
import org.aesh.util.progress.ProgressBarStyle;
256+
257+
@CommandDefinition(name = "process", description = "Process files")
258+
public class ProcessCommand implements Command<CommandInvocation> {
259+
260+
@Override
261+
public CommandResult execute(CommandInvocation invocation) {
262+
List<File> files = getFiles();
263+
264+
ProgressBar progress = ProgressBar.builder()
265+
.shell(invocation.getShell())
266+
.total(files.size())
267+
.label("Processing")
268+
.style(ProgressBarStyle.UNICODE)
269+
.showRatio(true)
270+
.build();
271+
272+
for (File file : files) {
273+
processFile(file);
274+
progress.step();
275+
}
276+
progress.complete("Done! Processed " + files.size() + " files.");
277+
278+
return CommandResult.SUCCESS;
279+
}
280+
}
281+
```
282+
283+
The key calls in the chain:
284+
285+
| Call | Returns | Purpose |
286+
|------|---------|---------|
287+
| `invocation.getShell()` | `Shell` | Access the terminal |
288+
| `shell.size()` | `Size` | Get terminal dimensions (used automatically) |
289+
| `progress.step()` | `void` | Advance and re-render the bar |
290+
| `progress.complete()` | `void` | Fill to 100% and move to next line |
291+
292+
## Edge Cases
293+
294+
| Scenario | Behavior |
295+
|----------|----------|
296+
| `total` is 0 | Renders as 100% (avoids division by zero) |
297+
| `current` exceeds `total` | Clamped to 100% |
298+
| Very narrow terminal | Bar width clamped to minimum of 10 characters |
299+
| `shell` is null | Update methods are no-ops; `render()` still works for testing |
300+
301+
## How It Works
302+
303+
Each call to `step()`, `update()`, or `complete()` re-renders the bar on the current line using ANSI escape sequences from `org.aesh.terminal.utils.ANSI`:
304+
305+
1. `ANSI.CURSOR_START` — moves the cursor to the beginning of the line
306+
2. `ANSI.ERASE_WHOLE_LINE` — clears the entire line
307+
3. `Shell.write(String)` — writes the formatted bar string
308+
309+
This creates a smooth in-place update effect without scrolling the terminal.
310+
311+
## API Reference
312+
313+
### ProgressBar
314+
315+
| Method | Description |
316+
|--------|-------------|
317+
| `ProgressBar.builder()` | Create a builder for fluent configuration |
318+
| `update(long)` | Set absolute progress and re-render |
319+
| `step()` | Increment by 1 and re-render |
320+
| `step(long)` | Increment by n and re-render |
321+
| `complete()` | Fill to 100% and print a newline |
322+
| `complete(String)` | Replace the bar with a completion message |
323+
324+
### ProgressBarStyle
325+
326+
| Style | Description |
327+
|-------|-------------|
328+
| `ASCII` | `#` fill, `-` empty, `[` `]` brackets |
329+
| `UNICODE` | `` fill, `` empty, `` `` brackets |
330+
| `SIMPLE` | `=` fill, space empty, `[` `]` brackets |
331+
| `ARROW` | `=` fill with `>` tip, space empty, `[` `]` brackets |
332+
333+
## See Also
334+
335+
- [Table Display](table) — structured data rendering utility
336+
- [CommandInvocation API](command-invocation) — accessing the Shell in commands

0 commit comments

Comments
 (0)