Skip to content

CAMEL-23237: camel-jbang - Adapt CLI table output to terminal width#22219

Merged
gnodet merged 1 commit intomainfrom
spiced-path
Mar 25, 2026
Merged

CAMEL-23237: camel-jbang - Adapt CLI table output to terminal width#22219
gnodet merged 1 commit intomainfrom
spiced-path

Conversation

@gnodet
Copy link
Contributor

@gnodet gnodet commented Mar 24, 2026

Summary

  • Add TerminalWidthHelper utility that detects terminal width via JLine and provides flexWidth() / scaleWidth() methods for adaptive column sizing
  • Update 33 CLI command files to use terminal-width-aware column widths instead of hardcoded values
  • Two scaling strategies: flexWidth() for single flexible columns (NO_BORDERS tables), scaleWidth() for proportional scaling of all columns (FANCY_ASCII tables)
  • Add comprehensive unit tests for TerminalWidthHelper

Rendering comparison

See the rendering details comment for before/after table comparisons at 85-col and 120-col terminals.

Test plan

  • TerminalWidthHelperTest — 18 tests covering flexWidth, scaleWidth, border overhead, and integration scenarios
  • Full camel-jbang-core test suite passes (334 tests)
  • CI: JDK 17, 21, 25 builds pass

@github-actions
Copy link
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using build-all, build-dependents, skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions github-actions bot added the dsl label Mar 24, 2026
@github-actions
Copy link
Contributor

🧪 CI tested the following changed modules:

  • dsl/camel-jbang/camel-jbang-core
All tested modules (26 modules)
  • Camel :: Coverage
  • Camel :: Coverage [pom]
  • Camel :: JBang :: Core
  • Camel :: JBang :: Core [jar]
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: Integration tests [jar]
  • Camel :: JBang :: MCP
  • Camel :: JBang :: MCP [jar]
  • Camel :: JBang :: Main
  • Camel :: JBang :: Main [jar]
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Edit [jar]
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Generate [jar]
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: Kubernetes [jar]
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: Route Parser [jar]
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Testing [jar]
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: JBang :: Plugin :: Validate [jar]
  • Camel :: Launcher
  • Camel :: Launcher [jar]
  • Camel :: Launcher :: Container
  • Camel :: Launcher :: Container [pom]

@gnodet gnodet force-pushed the spiced-path branch 3 times, most recently from c40c1c5 to 47abe10 Compare March 24, 2026 09:36
@gnodet
Copy link
Contributor Author

gnodet commented Mar 24, 2026

Rendering details — Claude Code on behalf of Guillaume Nodet

camel doc on 85-column terminal

Before (hardcoded column widths — table is wider than terminal, lines wrap badly):

BEFORE: `camel doc` on 85-column terminal (hardcoded column widths)
─────────────────────────────────────────────────────────────────────────────────────
╔═══════════════════╤════════════════════════════════════════════════════════════════
════════════════╤═════════════════════════╤════════╗
║ NAME              │ DESCRIPTION                                                    
                │ DEFAULT                 │ TYPE   ║
╠═══════════════════╪════════════════════════════════════════════════════════════════
════════════════╪═════════════════════════╪════════╣
║ brokers           │ URL of the Kafka brokers to use. The format is host1:port1,host
2:port2         │                         │ String ║
╟───────────────────┼────────────────────────────────────────────────────────────────
────────────────┼─────────────────────────┼────────╢
║ topic             │ Name of the topic to use. On the consumer you can use comma to 
separate        │                         │ String ║
║                   │ multiple topics.                                               
                │                         │        ║
╟───────────────────┼────────────────────────────────────────────────────────────────
────────────────┼─────────────────────────┼────────╢
║ groupId           │ A string that uniquely identifies the group of consumer process
es              │ myGroup                 │ String ║
╟───────────────────┼────────────────────────────────────────────────────────────────
────────────────┼─────────────────────────┼────────╢
║ keyDeserializer   │ Deserializer class for key                                     
                │ org.apache.kafka.common │ String ║
║                   │                                                                
                │ .serialization.StringDe │        ║
║                   │                                                                
                │ serializer              │        ║
╟───────────────────┼────────────────────────────────────────────────────────────────
────────────────┼─────────────────────────┼────────╢
║ valueDeserializer │ Deserializer class for value                                   
                │ org.apache.kafka.common │ String ║
║                   │                                                                
                │ .serialization.StringDe │        ║
║                   │                                                                
                │ serializer              │        ║
╟───────────────────┼────────────────────────────────────────────────────────────────
────────────────┼─────────────────────────┼────────╢
║ autoOffsetReset   │ What to do when there is no initial offset in ZooKeeper        
                │ latest                  │ String ║
╟───────────────────┼────────────────────────────────────────────────────────────────
────────────────┼─────────────────────────┼────────╢
║ consumersCount    │ The number of consumers that connect to kafka server           
                │ 1                       │ int    ║
╚═══════════════════╧════════════════════════════════════════════════════════════════
════════════════╧═════════════════════════╧════════╝


After (adaptive widths — table fits within 85 columns):

AFTER: `camel doc` on 85-column terminal (adaptive widths)
─────────────────────────────────────────────────────────────────────────────────────
╔═══════════════╤══════════════════════════════════╤══════════╤════════╗
║ NAME          │ DESCRIPTION                      │ DEFAULT  │ TYPE   ║
╠═══════════════╪══════════════════════════════════╪══════════╪════════╣
║ brokers       │ URL of the Kafka brokers to use. │          │ String ║
║               │ The format is                    │          │        ║
║               │ host1:port1,host2:port2          │          │        ║
╟───────────────┼──────────────────────────────────┼──────────┼────────╢
║ topic         │ Name of the topic to use. On the │          │ String ║
║               │ consumer you can use comma to    │          │        ║
║               │ separate multiple topics.        │          │        ║
╟───────────────┼──────────────────────────────────┼──────────┼────────╢
║ groupId       │ A string that uniquely           │ myGroup  │ String ║
║               │ identifies the group of consumer │          │        ║
║               │ processes                        │          │        ║
╟───────────────┼──────────────────────────────────┼──────────┼────────╢
║ keyDeserializ │ Deserializer class for key       │ org.apac │ String ║
║ er            │                                  │ he.kafka │        ║
║               │                                  │ .common. │        ║
║               │                                  │ serializ │        ║
║               │                                  │ ation.St │        ║
║               │                                  │ ringDese │        ║
║               │                                  │ rializer │        ║
╟───────────────┼──────────────────────────────────┼──────────┼────────╢
║ valueDeserial │ Deserializer class for value     │ org.apac │ String ║
║ izer          │                                  │ he.kafka │        ║
║               │                                  │ .common. │        ║
║               │                                  │ serializ │        ║
║               │                                  │ ation.St │        ║
║               │                                  │ ringDese │        ║
║               │                                  │ rializer │        ║
╟───────────────┼──────────────────────────────────┼──────────┼────────╢
║ autoOffsetRes │ What to do when there is no      │ latest   │ String ║
║ et            │ initial offset in ZooKeeper      │          │        ║
╟───────────────┼──────────────────────────────────┼──────────┼────────╢
║ consumersCoun │ The number of consumers that     │ 1        │ int    ║
║ t             │ connect to kafka server          │          │        ║
╚═══════════════╧══════════════════════════════════╧══════════╧════════╝


camel doc on 120-column terminal

After (adaptive widths — uses the extra space for wider columns):

AFTER: `camel doc` on 120-column terminal (adaptive widths)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╔═══════════════════╤═══════════════════════════════════════════════════╤════════════════╤════════╗
║ NAME              │ DESCRIPTION                                       │ DEFAULT        │ TYPE   ║
╠═══════════════════╪═══════════════════════════════════════════════════╪════════════════╪════════╣
║ brokers           │ URL of the Kafka brokers to use. The format is    │                │ String ║
║                   │ host1:port1,host2:port2                           │                │        ║
╟───────────────────┼───────────────────────────────────────────────────┼────────────────┼────────╢
║ topic             │ Name of the topic to use. On the consumer you can │                │ String ║
║                   │ use comma to separate multiple topics.            │                │        ║
╟───────────────────┼───────────────────────────────────────────────────┼────────────────┼────────╢
║ groupId           │ A string that uniquely identifies the group of    │ myGroup        │ String ║
║                   │ consumer processes                                │                │        ║
╟───────────────────┼───────────────────────────────────────────────────┼────────────────┼────────╢
║ keyDeserializer   │ Deserializer class for key                        │ org.apache.kaf │ String ║
║                   │                                                   │ ka.common.seri │        ║
║                   │                                                   │ alization.Stri │        ║
║                   │                                                   │ ngDeserializer │        ║
╟───────────────────┼───────────────────────────────────────────────────┼────────────────┼────────╢
║ valueDeserializer │ Deserializer class for value                      │ org.apache.kaf │ String ║
║                   │                                                   │ ka.common.seri │        ║
║                   │                                                   │ alization.Stri │        ║
║                   │                                                   │ ngDeserializer │        ║
╟───────────────────┼───────────────────────────────────────────────────┼────────────────┼────────╢
║ autoOffsetReset   │ What to do when there is no initial offset in     │ latest         │ String ║
║                   │ ZooKeeper                                         │                │        ║
╟───────────────────┼───────────────────────────────────────────────────┼────────────────┼────────╢
║ consumersCount    │ The number of consumers that connect to kafka     │ 1              │ int    ║
║                   │ server                                            │                │        ║
╚═══════════════════╧═══════════════════════════════════════════════════╧════════════════╧════════╝

camel ps on 85-column terminal

Before (hardcoded NAME max-width 40 — columns spill past terminal):

BEFORE: `camel ps` on 85-column terminal (hardcoded widths)
─────────────────────────────────────────────────────────────────────────────────────
  PID   NAME                  READY   STATUS    AGE   TOTAL  FAIL  INFLIGHT 
 12345  my-kafka-integration   1/1    Running  2h30m   1542     3         0 
 12346  rest-api-service       1/1    Running  1h15m    891     0         2 
 12347  file-processor-route   0/1   Starting     5s      0     0         0 


After (adaptive NAME width — truncated with ellipsis to fit):

AFTER: `camel ps` on 85-column terminal (adaptive widths)
─────────────────────────────────────────────────────────────────────────────────────
  PID   NAME           READY   STATUS    AGE   TOTAL  FAIL  INFLIGHT 
 12345  my-kafka-int…   1/1    Running  2h30m   1542     3         0 
 12346  rest-api-ser…   1/1    Running  1h15m    891     0         2 
 12347  file-process…   0/1   Starting     5s      0     0         0 


- Add TerminalWidthHelper utility that detects terminal width via JLine
  and provides methods for proportional column width scaling
- Add terminalWidth() method to CamelCommand base class
- Update all 33 command files that produce AsciiTable output to adapt
  their column widths based on the detected terminal width
- CatalogDoc tables use proportional scaling across all columns for
  FANCY_ASCII bordered tables (most visible in demos)
- Process/action commands scale their flexible columns (URI, DESCRIPTION,
  MESSAGE, etc.) while keeping small fixed columns unchanged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet merged commit 5a1a836 into main Mar 25, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants