[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Documentation of gb.args and gb.highlight - Gambas


Hello everyone,

I would like to share with you 2 little documentations regarding gb.args and gb.highlight component in Gambas.

As the original documentations for both components were not enough to clearly use them (on my opinion), I decided to created two new little docs with more details.

I have attached both of them docs in format Markdown Gambas (like that they could be added in the wiki of Gambas easily), and also both docs in HTML to show you what they are.

Let me know if you find glitches or errors in those docs please.


Now the question is where to place them into the Gambas Wiki, which section will be the best please ?


--
Olivier

Usage guide for the gb.highlight component with TextEditor

1. Basic concepts

To highlight text, the engine needs two things:
  1. A syntax file (.highlight): It contains the regular expressions and rules that split the text and assign it logical "States" or "Styles" (e.g. Keyword, String, Comment).

  2. A theme file (.theme): It maps these logical "States" to actual colors and font styles (Bold, Italic, etc.).

Create a syntax file (mylanguage.highlight)

Rules are read from top to bottom (the first that matches is applied).

#Basic example
comment {Comment}:
  From "//"

string.double {String}:
  From "\"" To "\""

keyword {Keyword}:
  keyword If Else Then For While return

Create a theme file (mylanguage.theme)

Configuration file mapping logical styles to colors.

[Gambas Highlighting Theme 1.0]
Background = "#FFFFFF"
Normal = "#000000"
Comment = "#008000,Oblique"
String = "#A31515"
Keyword = "#0000FF,Bold"

2. The Theme loading "Trap" (MUST READ)

The most common mistake when assigning a custom theme is using the method this way:

TextEditor1.Theme.Load("./mylanguage.theme")(DOES NOT WORK AS EXPECTED)

Why

Gambas's architecture defines TextHighlighterTheme.Load(...) as a static method that generates and returns a brand new object TextHighlighterTheme. If the return is not explicitly captured to overwrite the editor's theme, the file is read into the void and the editor does not change color.

The right way:

You must replace the editor's Theme object with the new generated object:

TextEditor1.Theme = TextHighlighterTheme.Load("./mylanguage.theme")

3. The initialization "Trap" (Form_Open)

When the Form_Open event is triggered, the TextEditor widget is not yet fully drawn on the screen. If you apply syntax highlighting at this precise moment, the internal C++ cache might ignore the colors.

The right way:

The recommended trick by the Gambas IDE is to defer the loading of the editor's configuration by a few milliseconds using a Timer object.

4. Complete and Robust Example Code

Here is the standard and robust code to implement your custom syntax highlighting in your form:

' Declaration of the Timer to defer the display
Private $TimerOpening As Timer

Public Sub Form_Open()

  ' 1. Registration of the syntax in the global engine
  ' (Internal_name, Display_name, File_path)
  If Not TextHighlighter.List.Exist("mylanguage") Then 
    TextHighlighter.Register("mylanguage", "My Language", "./mylanguage.highlight")
  End If

  ' 2. Launch the Timer to configure the editor after opening the window
  $TimerOpening = New Timer As "TimerOpening"
  $TimerOpening.Delay = 10 ' 10 milliseconds are enough
  $TimerOpening.Start()

End


Public Sub TimerOpening_Timer()
  
  $TimerOpening.Stop()

  ' Make sure our language has been registered properly
  If TextHighlighter.List.Exist("mylanguage") Then 
    
    ' A. Tell the editor which syntax to use
    TextEditor1.Highlight = "mylanguage"
    
    ' B. Replace the WHOLE Theme object of the editor with the new loaded Theme
    If Exist("./mylanguage.theme") Then
      TextEditor1.Theme = TextHighlighterTheme.Load("./mylanguage.theme")
    Endif
    
    ' C. Force redrawing of the editor with these new rules
    TextEditor1.Refresh()
    
  End If
  
  ' Optional: Load your text and give focus
  ' TextEditor1.Text = File.Load("my_code.txt")
  TextEditor1.Goto(0, 0, True)
  TextEditor1.SetFocus()

End

Summary of key points

# Usage guide for the \
# [`gb.highlight` component with `TextEditor`]

@{index}

- The `gb.highlight` component is the modern syntax highlighting engine for Gambas (which replaces the old `gb.eval.highlight`). It works together with the `gb.form.editor` component (which provides the `TextEditor` widget).

- Although very powerful, its internal operation with the `TextEditor` has a few architectural subtleties that are crucial to understand to prevent custom themes from not being displayed.

## [1. Basic concepts]

To highlight text, the engine needs two things:
1. **A syntax file (`.highlight`)**: It contains the regular expressions and rules that split the text and assign it logical "States" or "Styles" (e.g. *Keyword*, *String*, *Comment*).
2. **A theme file (`.theme`)**: It maps these logical "States" to actual colors and font styles (Bold, Italic, etc.).

### [Create a syntax file (`mylanguage.highlight`)]

Rules are read from top to bottom (the first that matches is applied).

[[ code sh
#Basic example
comment {Comment}:
  From "//"

string.double {String}:
  From "\"" To "\""

keyword {Keyword}:
  keyword If Else Then For While return
]]

### [Create a theme file (`mylanguage.theme`)]

Configuration file mapping logical styles to colors.

[[ code sh
[Gambas Highlighting Theme 1.0]
Background = "#FFFFFF"
Normal = "#000000"
Comment = "#008000,Oblique"
String = "#A31515"
Keyword = "#0000FF,Bold"
]]

## [2. The Theme loading "Trap" (MUST READ)]

The most common mistake when assigning a custom theme is using the method this way: \ 

`TextEditor1.Theme.Load("./mylanguage.theme")`  ❌ **(DOES NOT WORK AS EXPECTED)**

**Why**

Gambas's architecture defines `TextHighlighterTheme.Load(...)` as a **static method** that generates and *returns a brand new object* `TextHighlighterTheme`. If the return is not explicitly captured to overwrite the editor's theme, the file is read into the void and the editor does not change color.

**The right way:**

You must replace the editor's Theme object with the new generated object:

`TextEditor1.Theme = TextHighlighterTheme.Load("./mylanguage.theme")` ✅


## [3. The initialization "Trap" (Form_Open)]

When the `Form_Open` event is triggered, the `TextEditor` widget is not yet fully drawn on the screen. If you apply syntax highlighting at this precise moment, the internal C++ cache might ignore the colors.

**The right way:**

The recommended trick by the Gambas IDE is to defer the loading of the editor's configuration by a few milliseconds using a `Timer` object.

## [4. Complete and Robust Example Code]

Here is the standard and robust code to implement your custom syntax highlighting in your form:

[[ code gambas
' Declaration of the Timer to defer the display
Private $TimerOpening As Timer

Public Sub Form_Open()

  ' 1. Registration of the syntax in the global engine
  ' (Internal_name, Display_name, File_path)
  If Not TextHighlighter.List.Exist("mylanguage") Then 
    TextHighlighter.Register("mylanguage", "My Language", "./mylanguage.highlight")
  End If

  ' 2. Launch the Timer to configure the editor after opening the window
  $TimerOpening = New Timer As "TimerOpening"
  $TimerOpening.Delay = 10 ' 10 milliseconds are enough
  $TimerOpening.Start()

End


Public Sub TimerOpening_Timer()
  
  $TimerOpening.Stop()

  ' Make sure our language has been registered properly
  If TextHighlighter.List.Exist("mylanguage") Then 
    
    ' A. Tell the editor which syntax to use
    TextEditor1.Highlight = "mylanguage"
    
    ' B. Replace the WHOLE Theme object of the editor with the new loaded Theme
    If Exist("./mylanguage.theme") Then
      TextEditor1.Theme = TextHighlighterTheme.Load("./mylanguage.theme")
    Endif
    
    ' C. Force redrawing of the editor with these new rules
    TextEditor1.Refresh()
    
  End If
  
  ' Optional: Load your text and give focus
  ' TextEditor1.Text = File.Load("my_code.txt")
  TextEditor1.Goto(0, 0, True)
  TextEditor1.SetFocus()

End
]]

### Summary of key points
* Always enable the `gb.highlight` and `gb.form.editor` components.
* Use `TextHighlighter.Register` to teach your syntax to the global engine.
* Assign the `TextEditor.Highlight` **before** loading the `TextEditor.Theme`.
* Capture the return of `TextHighlighterTheme.Load(...)` to inject it into `TextEditor.Theme`.
* Use a short `Timer` to apply everything once the view (GUI) is ready.

📚 The gb.args component — Complete guide

1. Context: two argument levels

In Gambas, there are two mechanisms to receive command line arguments:

A. The Args class from the gb component (basic)

' Args is a static read-only array
Print Args.Count      ' Total number of arguments (includes the program name)
Print Args.Max        ' Index of the last argument (= Args.Count - 1)
Print Args.All.Join(" | ")  ' All arguments separated by " | "
Print Args[0]         ' Program name
Print Args[1]         ' First passed argument

Example:

$ gbx3 $HOME/MyApp -- hello world 42

Print Args.Count     ' → 4
Print Args.Max       ' → 3
Print Args.All.Join(" | ")  ' → "MyApp | hello | world | 42"
Print Args[0]        ' → "MyApp"
Print Args[1]        ' → "hello"
Print Args[2]        ' → "world"
Print Args[3]        ' → "42"

⚠️ Args from gb is raw: everything is a string, no options parsing.

B. The Args class from the gb.args component (sophisticated)

The gb.args component (available since Gambas 3.4) provides a static Args class with 5 methods to properly parse options and arguments.

2. The 5 methods of the Args class (gb.args)

Method Role
Begin([Usage As String]) Starts parsing. Optional: help text
End() As String[] Ends parsing, returns raw arguments
Get(short, long, desc, argName) As String String option
GetFloat(short, long[, desc, argName, default]) As Float Float option
GetInteger(short, long[, desc, argName, default]) As Integer Integer option
Has(short, long[, desc]) As Boolean Boolean option (flag)

3. Example 1: Minimal program with options

' Main Module (Main)
Public Sub Main()
  Args.Begin()
  ' String option : -f / --file
  Dim sFichier As String
  sFichier = Args.Get("f", "file", "Input file", "FILE")
  ' Boolean option (flag) : -v / --verbose
  Dim bVerbose As Boolean
  bVerbose = Args.Has("v", "verbose")
  ' Integer option with default value
  Dim iTaille As Integer
  iTaille = Args.GetInteger("s", "size", "Buffer size", "SIZE", 1024)
  ' Retrieve the remaining arguments (after --)
  Dim aArgs As String[]
  aArgs = Args.End()
  ' Display
  Print "File: " & sFichier
  Print "Verbose: " & bVerbose
  Print "Size: " & iTaille
  Print "Remaining arguments: " & aArgs.Join(", ")
End

Usage:

$ gbx3 $HOME/MyApp -- -f config.txt -2048 -- input1.txt input2.txt
File: config.txt
Verbose: False
Size: 2048
Remaining arguments: input1.txt, input2.txt

$ gbx3 $HOME/MyApp -- --verbose --file=data.ini -- arg1 arg2 arg3
File: data.ini
Verbose: True
Size: 1024
Remaining arguments: arg1, arg2, arg3

4. Example 2: With complete help text

Public Sub Main()
  Dim sMode As String
  Dim fRatio As Float
  Dim bForce As Boolean
  Dim aArgs As String[]
  Args.Begin(SetHelpText())
  sMode = Args.Get("m", "mode", "Processing mode (fast|normal|deep)", "MODE")
  fRatio = Args.GetFloat("r", "ratio", "Compression ratio", "RATIO", 0.75)
  bForce = Args.Has("f", "force", "Force processing without confirmation")
  aArgs = Args.End()
  Print "Mode: " & sMode
  Print "Ratio: " & fRatio
  Print "Force: " & bForce
  Print "Args: " & aArgs.Join(", ")
End

Private Function SetHelpText() As String
  Dim sText As String
  sText &= gb.NewLine
  sText &= "MyApp - Image processing" & gb.NewLine
  sText &= gb.NewLine
  sText &= "Usage: gbx3 /path/to/project -- [options] [-- <args>]" & gb.NewLine
  sText &= gb.NewLine
  sText &= "Example: gbx3 $HOME/MyApp -- -m deep -r 0.5 -- img1.png img2.png" & gb.NewLine
  sText &= gb.NewLine
  sText &= "Options:" & gb.NewLine
  sText &= " -m --mode <MODE>          Processing mode (fast|normal|deep)" & gb.NewLine
  sText &= " -r --ratio <RATIO>        Compression ratio (default: 0.75)" & gb.NewLine
  sText &= " -f --force                Force processing" & gb.NewLine
  sText &= " -V --version              Display the version" & gb.NewLine
  sText &= " -h --help                 Display this help" & gb.NewLine
  Return sText
End

--help output:

MyApp - Image processing
Usage: gbx3 /path/to/project -- [options] [-- <args>]
Example: gbx3 $HOME/MyApp -- -m deep -0.5 -- img1.png img2.png
Options:
 ---mode <MODE>          Processing mode (fast|normal|deep)
 ---ratio <RATIO>        Compression ratio (default: 0.75)
 ---force                Force processing
 ---version              Display the version
 ---help                 Display this help

5. Example 3: Options with default values and multiple types

Public Sub Main()
  Dim sHost As String
  Dim iPort As Integer
  Dim fTimeout As Float
  Dim bDebug As Boolean
  Dim bQuiet As Boolean
  Dim aUrls As String[]
  Args.Begin("MyApp - HTTP Client")
  ' String with default
  sHost = Args.Get("h", "host", "Target server", "HOST", "localhost")
  ' Integer with default
  iPort = Args.GetInteger("p", "port", "Server port", "PORT", 8080)
  ' Float with default
  fTimeout = Args.GetFloat("t", "timeout", "Timeout in seconds", "TIMEOUT", 30.0)
  ' Boolean flags
  bDebug = Args.Has("d", "debug")
  bQuiet = Args.Has("q", "quiet")
  ' Remaining arguments (URLs)
  aUrls = Args.End()
  If Not bQuiet Then
    Print "Connecting to " & sHost & ":" & iPort
    Print "Timeout: " & fTimeout & "s"
    Print "Debug: " & bDebug
    Print "URLs: " & aUrls.Join(", ")
  Endif
End

Usages:

# With all default options
$ gbx3 $HOME/MyApp -- --host api.example.com --port 443 -- https://api.example.com/data
Connecting to api.example.com:443
Timeout: 30.0s
Debug: False
URLs: https://api.example.com/data

# With flags and custom values
$ gbx3 $HOME/MyApp -- --60.5 -- https://api1.com https://api2.com
Connecting to localhost:8080
Timeout: 60.5s
Debug: True
URLs: https://api1.com, https://api2.com

6. Example 4: Option without value (flag) + option with mandatory value

Public Sub Main()
  Dim sOutput As String
  Dim bList As Boolean
  Dim bVersion As Boolean
  Dim aFiles As String[]
  Args.Begin("MyApp - File manager")
  ' Option with mandatory value (no default)
  sOutput = Args.Get("o", "output", "Output file", "FILE")
  ' Flag (boolean)
  bList = Args.Has("l", "list", "List files")
  ' Note: -V and -h are handled automatically by gb.args
  aFiles = Args.End()
  If bList Then
    Print "File list:"
    Print aFiles.Join(gb.NewLine)
  Else
    Print "Output to: " & sOutput
  Endif
End

Usage:

# Mandatory -o option
$ gbx3 $HOME/MyApp -- -o result.txt file1.txt file2.txt
Output to: result.txt

# -l flag
$ gbx3 $HOME/MyApp -- --- file1.txt file2.txt
File list:
file1.txt
file2.txt

# Version (handled automatically)
$ gbx3 $HOME/MyApp -- --version
0.1.0

# Help (handled automatically)
$ gbx3 $HOME/MyApp -- -h
MyApp - File manager
Usage: gbx3 /path/to/project -- [options] [-- <args>]
Options:
 ---output <FILE>        Output file
 ---list                 List files
 ---version              Display the version
 ---help                 Display this help

7. Example 5: Error handling

Public Sub Main()
  Try
    Args.Begin(SetHelpText())
    Dim sMode As String
    sMode = Args.Get("m", "mode", "Mode (fast|normal|deep)", "MODE")
    ' Default value if option not specified
    If sMode = "" Then
      sMode = "normal"
    Endif
    Args.End()
    Print "Selected mode: " & sMode
  Catch
    ' gb.args exits with code 1 in case of a parsing error
    ' (unknown option, missing value, etc.)
    Print "Command line error: " & Error.Text
    Quit 1
  End
End

Common errors:

# Unknown option → exits with code 1
$ gbx3 $HOME/MyApp -- -z badoption
gb.args: unknown option 'z'
# Option with missing value → exits with code 1
$ gbx3 $HOME/MyApp -- -m
gb.args: option 'm' requires an argument
# -o option without value → default value = ""
$ gbx3 $HOME/MyApp -- --output
Output to: (empty)

8. Example 6: Complete professional CLI program

' Main Module
Public Sub Main()
  Dim sAction As String
  Dim sConfig As String
  Dim iVerbosity As Integer
  Dim bDryRun As Boolean
  Dim bVersion As Boolean
  Dim aTargets As String[]
  Args.Begin(BuildHelp())
  sAction = Args.Get("a", "action", "Action (init|deploy|rollback|status)", "ACTION")
  sConfig = Args.Get("c", "config", "Configuration file", "CONFIG", "/etc/myapp/config.ini")
  iVerbosity = Args.GetInteger("v", "verbose", "Verbosity level (0-3)", "LEVEL", 0)
  bDryRun = Args.Has("n", "dry-run", "Dry run")
  bVersion = Args.Has("", "version")
  aTargets = Args.End()
  Select Case sAction
    Case "init"
      Print "[INIT] Configuration: " & sConfig
    Case "deploy"
      Print "[DEPLOY] Target: " & aTargets.Join(", ")
      If bDryRun Then Print "  (DRY-RUN - no real changes)"
    Case "rollback"
      Print "[ROLLBACK] Previous version restored"
    Case "status"
      Print "[STATUS] In progress..."
    Case ""
      Print "Error: --action is mandatory"
      Print "Use -h for help"
      Quit 1
    Case Else
      Print "Error: unknown action '" & sAction & "'"
      Quit 1
  End Select
  Print "Verbosity: " & iVerbosity
End

Private Function BuildHelp() As String
  Dim h As String
  h &= gb.NewLine
  h &= "MyApp v1.2.3 - Deployment manager" & gb.NewLine
  h &= gb.NewLine
  h &= "Usage: gbx3 /project -- [options] [-- <targets>]" & gb.NewLine
  h &= gb.NewLine
  h &= "Mandatory actions:" & gb.NewLine
  h &= "  --action <ACTION>     init | deploy | rollback | status" & gb.NewLine
  h &= gb.NewLine
  h &= "Options:" & gb.NewLine
  h &= "  -c --config <FILE>    Config (default: /etc/myapp/config.ini)" & gb.NewLine
  h &= "  -v --verbose <LEVEL>  Verbosity 0-3 (default: 0)" & gb.NewLine
  h &= "  -n --dry-run          Dry run" & gb.NewLine
  h &= "  -V --version          Display the version" & gb.NewLine
  h &= "  -h --help             Display this help" & gb.NewLine
  h &= gb.NewLine
  h &= "Example:" & gb.NewLine
  h &= "  gbx3 $HOME/MyApp -- -a deploy -v 2 -n -- web01 web02" & gb.NewLine
  Return h
End

Usage:

$ gbx3 $HOME/MyApp -- -a deploy -2 --- web01 web02
[DEPLOY] Target: web01, web02
  (DRY-RUN - no real changes)
Verbosity: 2

$ gbx3 $HOME/MyApp -- -a status
[STATUS] In progress...
Verbosity: 0

9. Important usage rules

Point Detail
-- separator Mandatory between options and raw arguments
-V/--version Handled automatically, no need for Has()
-h/--help Handled automatically, displays the Begin() text
Parsing error Exits with code 1 automatically
Call order Begin()Get/GetFloat/GetInteger/Has()End()
Missing value For Get(): returns ""
For GetInteger/GetFloat: returns the default value
Component Add gb.args in the .project file

10. Quick comparison

Aspect Args (gb) Args (gb.args)
Options parsing ❌ No ✅ Yes
Types (int/float/bool) ❌ All string ✅ Typed
Default values
Automatic help
Automatic version
Simplicity ✅ Very simple ⚠️ More code

## 📚 The `gb.args` component — Complete guide

@{index}

### [1. Context: two argument levels]

In Gambas, there are **two mechanisms** to receive command line arguments:

#### [A. The `Args` class from the `gb` component (basic)]

[[ code gambas
' Args is a static read-only array
Print Args.Count      ' Total number of arguments (includes the program name)
Print Args.Max        ' Index of the last argument (= Args.Count - 1)
Print Args.All.Join(" | ")  ' All arguments separated by " | "
Print Args[0]         ' Program name
Print Args[1]         ' First passed argument
]]

**Example:**

[[ code sh
$ gbx3 $HOME/MyApp -- hello world 42
]]

[[ code gambas
Print Args.Count     ' → 4
Print Args.Max       ' → 3
Print Args.All.Join(" | ")  ' → "MyApp | hello | world | 42"
Print Args[0]        ' → "MyApp"
Print Args[1]        ' → "hello"
Print Args[2]        ' → "world"
Print Args[3]        ' → "42"
]]

> ⚠️ `Args` from `gb` is **raw**: everything is a string, no options parsing.

#### [B. The `Args` class from the `gb.args` component (sophisticated)]

The `gb.args` component (available since Gambas 3.4) provides a **static** `Args` class with 5 methods to properly parse options and arguments.

### [2. The 5 methods of the `Args` class (gb.args)]

[[
Method
--
Role
==
`Begin([Usage As String])`
--
Starts parsing. Optional: help text
==
`End() As String[]`
--
Ends parsing, returns raw arguments
==
`Get(short, long, desc, argName) As String`
--
String option
==
`GetFloat(short, long[, desc, argName, default]) As Float`
--
Float option
==
`GetInteger(short, long[, desc, argName, default]) As Integer`
--
Integer option
==
`Has(short, long[, desc]) As Boolean`
--
Boolean option (flag)
]]


### [3. Example 1: Minimal program with options]

[[ code gambas
' Main Module (Main)
Public Sub Main()
  Args.Begin()
  ' String option : -f / --file
  Dim sFichier As String
  sFichier = Args.Get("f", "file", "Input file", "FILE")
  ' Boolean option (flag) : -v / --verbose
  Dim bVerbose As Boolean
  bVerbose = Args.Has("v", "verbose")
  ' Integer option with default value
  Dim iTaille As Integer
  iTaille = Args.GetInteger("s", "size", "Buffer size", "SIZE", 1024)
  ' Retrieve the remaining arguments (after --)
  Dim aArgs As String[]
  aArgs = Args.End()
  ' Display
  Print "File: " & sFichier
  Print "Verbose: " & bVerbose
  Print "Size: " & iTaille
  Print "Remaining arguments: " & aArgs.Join(", ")
End
]]

**Usage:**

[[ code sh
$ gbx3 $HOME/MyApp -- -f config.txt -s 2048 -- input1.txt input2.txt
File: config.txt
Verbose: False
Size: 2048
Remaining arguments: input1.txt, input2.txt

$ gbx3 $HOME/MyApp -- --verbose --file=data.ini -- arg1 arg2 arg3
File: data.ini
Verbose: True
Size: 1024
Remaining arguments: arg1, arg2, arg3
]]
---
### [4. Example 2: With complete help text]

[[ code gambas
Public Sub Main()
  Dim sMode As String
  Dim fRatio As Float
  Dim bForce As Boolean
  Dim aArgs As String[]
  Args.Begin(SetHelpText())
  sMode = Args.Get("m", "mode", "Processing mode (fast|normal|deep)", "MODE")
  fRatio = Args.GetFloat("r", "ratio", "Compression ratio", "RATIO", 0.75)
  bForce = Args.Has("f", "force", "Force processing without confirmation")
  aArgs = Args.End()
  Print "Mode: " & sMode
  Print "Ratio: " & fRatio
  Print "Force: " & bForce
  Print "Args: " & aArgs.Join(", ")
End

Private Function SetHelpText() As String
  Dim sText As String
  sText &= gb.NewLine
  sText &= "MyApp - Image processing" & gb.NewLine
  sText &= gb.NewLine
  sText &= "Usage: gbx3 /path/to/project -- [options] [-- <args>]" & gb.NewLine
  sText &= gb.NewLine
  sText &= "Example: gbx3 $HOME/MyApp -- -m deep -r 0.5 -- img1.png img2.png" & gb.NewLine
  sText &= gb.NewLine
  sText &= "Options:" & gb.NewLine
  sText &= " -m --mode <MODE>          Processing mode (fast|normal|deep)" & gb.NewLine
  sText &= " -r --ratio <RATIO>        Compression ratio (default: 0.75)" & gb.NewLine
  sText &= " -f --force                Force processing" & gb.NewLine
  sText &= " -V --version              Display the version" & gb.NewLine
  sText &= " -h --help                 Display this help" & gb.NewLine
  Return sText
End
]]

**`--help` output:**

[[ code sh
MyApp - Image processing
Usage: gbx3 /path/to/project -- [options] [-- <args>]
Example: gbx3 $HOME/MyApp -- -m deep -r 0.5 -- img1.png img2.png
Options:
 -m --mode <MODE>          Processing mode (fast|normal|deep)
 -r --ratio <RATIO>        Compression ratio (default: 0.75)
 -f --force                Force processing
 -V --version              Display the version
 -h --help                 Display this help
]]

### [5. Example 3: Options with default values and multiple types]

[[ code gambas
Public Sub Main()
  Dim sHost As String
  Dim iPort As Integer
  Dim fTimeout As Float
  Dim bDebug As Boolean
  Dim bQuiet As Boolean
  Dim aUrls As String[]
  Args.Begin("MyApp - HTTP Client")
  ' String with default
  sHost = Args.Get("h", "host", "Target server", "HOST", "localhost")
  ' Integer with default
  iPort = Args.GetInteger("p", "port", "Server port", "PORT", 8080)
  ' Float with default
  fTimeout = Args.GetFloat("t", "timeout", "Timeout in seconds", "TIMEOUT", 30.0)
  ' Boolean flags
  bDebug = Args.Has("d", "debug")
  bQuiet = Args.Has("q", "quiet")
  ' Remaining arguments (URLs)
  aUrls = Args.End()
  If Not bQuiet Then
    Print "Connecting to " & sHost & ":" & iPort
    Print "Timeout: " & fTimeout & "s"
    Print "Debug: " & bDebug
    Print "URLs: " & aUrls.Join(", ")
  Endif
End
]]

**Usages:**

[[ code sh
# With all default options
$ gbx3 $HOME/MyApp -- --host api.example.com --port 443 -- https://api.example.com/data
Connecting to api.example.com:443
Timeout: 30.0s
Debug: False
URLs: https://api.example.com/data

# With flags and custom values
$ gbx3 $HOME/MyApp -- -d -t 60.5 -- https://api1.com https://api2.com
Connecting to localhost:8080
Timeout: 60.5s
Debug: True
URLs: https://api1.com, https://api2.com
]]

### [6. Example 4: Option without value (flag) + option with mandatory value]

[[ code gambas
Public Sub Main()
  Dim sOutput As String
  Dim bList As Boolean
  Dim bVersion As Boolean
  Dim aFiles As String[]
  Args.Begin("MyApp - File manager")
  ' Option with mandatory value (no default)
  sOutput = Args.Get("o", "output", "Output file", "FILE")
  ' Flag (boolean)
  bList = Args.Has("l", "list", "List files")
  ' Note: -V and -h are handled automatically by gb.args
  aFiles = Args.End()
  If bList Then
    Print "File list:"
    Print aFiles.Join(gb.NewLine)
  Else
    Print "Output to: " & sOutput
  Endif
End
]]

**Usage:**

[[ code sh
# Mandatory -o option
$ gbx3 $HOME/MyApp -- -o result.txt file1.txt file2.txt
Output to: result.txt

# -l flag
$ gbx3 $HOME/MyApp -- -l -- file1.txt file2.txt
File list:
file1.txt
file2.txt

# Version (handled automatically)
$ gbx3 $HOME/MyApp -- --version
0.1.0

# Help (handled automatically)
$ gbx3 $HOME/MyApp -- -h
MyApp - File manager
Usage: gbx3 /path/to/project -- [options] [-- <args>]
Options:
 -o --output <FILE>        Output file
 -l --list                 List files
 -V --version              Display the version
 -h --help                 Display this help
]]

### [7. Example 5: Error handling]

[[ code gambas
Public Sub Main()
  Try
    Args.Begin(SetHelpText())
    Dim sMode As String
    sMode = Args.Get("m", "mode", "Mode (fast|normal|deep)", "MODE")
    ' Default value if option not specified
    If sMode = "" Then
      sMode = "normal"
    Endif
    Args.End()
    Print "Selected mode: " & sMode
  Catch
    ' gb.args exits with code 1 in case of a parsing error
    ' (unknown option, missing value, etc.)
    Print "Command line error: " & Error.Text
    Quit 1
  End
End
]]

**Common errors:**

[[ code sh
# Unknown option → exits with code 1
$ gbx3 $HOME/MyApp -- -z badoption
gb.args: unknown option 'z'
# Option with missing value → exits with code 1
$ gbx3 $HOME/MyApp -- -m
gb.args: option 'm' requires an argument
# -o option without value → default value = ""
$ gbx3 $HOME/MyApp -- --output
Output to: (empty)
]]


### [8. Example 6: Complete professional CLI program]

[[ code gambas
' Main Module
Public Sub Main()
  Dim sAction As String
  Dim sConfig As String
  Dim iVerbosity As Integer
  Dim bDryRun As Boolean
  Dim bVersion As Boolean
  Dim aTargets As String[]
  Args.Begin(BuildHelp())
  sAction = Args.Get("a", "action", "Action (init|deploy|rollback|status)", "ACTION")
  sConfig = Args.Get("c", "config", "Configuration file", "CONFIG", "/etc/myapp/config.ini")
  iVerbosity = Args.GetInteger("v", "verbose", "Verbosity level (0-3)", "LEVEL", 0)
  bDryRun = Args.Has("n", "dry-run", "Dry run")
  bVersion = Args.Has("", "version")
  aTargets = Args.End()
  Select Case sAction
    Case "init"
      Print "[INIT] Configuration: " & sConfig
    Case "deploy"
      Print "[DEPLOY] Target: " & aTargets.Join(", ")
      If bDryRun Then Print "  (DRY-RUN - no real changes)"
    Case "rollback"
      Print "[ROLLBACK] Previous version restored"
    Case "status"
      Print "[STATUS] In progress..."
    Case ""
      Print "Error: --action is mandatory"
      Print "Use -h for help"
      Quit 1
    Case Else
      Print "Error: unknown action '" & sAction & "'"
      Quit 1
  End Select
  Print "Verbosity: " & iVerbosity
End

Private Function BuildHelp() As String
  Dim h As String
  h &= gb.NewLine
  h &= "MyApp v1.2.3 - Deployment manager" & gb.NewLine
  h &= gb.NewLine
  h &= "Usage: gbx3 /project -- [options] [-- <targets>]" & gb.NewLine
  h &= gb.NewLine
  h &= "Mandatory actions:" & gb.NewLine
  h &= "  --action <ACTION>     init | deploy | rollback | status" & gb.NewLine
  h &= gb.NewLine
  h &= "Options:" & gb.NewLine
  h &= "  -c --config <FILE>    Config (default: /etc/myapp/config.ini)" & gb.NewLine
  h &= "  -v --verbose <LEVEL>  Verbosity 0-3 (default: 0)" & gb.NewLine
  h &= "  -n --dry-run          Dry run" & gb.NewLine
  h &= "  -V --version          Display the version" & gb.NewLine
  h &= "  -h --help             Display this help" & gb.NewLine
  h &= gb.NewLine
  h &= "Example:" & gb.NewLine
  h &= "  gbx3 $HOME/MyApp -- -a deploy -v 2 -n -- web01 web02" & gb.NewLine
  Return h
End
]]

**Usage:**

[[ code sh
$ gbx3 $HOME/MyApp -- -a deploy -v 2 -n -- web01 web02
[DEPLOY] Target: web01, web02
  (DRY-RUN - no real changes)
Verbosity: 2

$ gbx3 $HOME/MyApp -- -a status
[STATUS] In progress...
Verbosity: 0
]]

### [9. Important usage rules]
[[
Point
--
Detail
==
**`--` separator**
--
Mandatory between options and raw arguments
==
**`-V/--version`**
--
Handled automatically, no need for `Has()`
==
**`-h/--help`**
--
Handled automatically, displays the `Begin()` text
==
**Parsing error**
--
Exits with code 1 automatically
==
**Call order**
--
`Begin()` → `Get/GetFloat/GetInteger/Has()` → `End()`
==
**Missing value**
--
For `Get()`: returns `""` \
For `GetInteger/GetFloat`: returns the default value
==
**Component**
--
Add `gb.args` in the `.project` file
]]


### [10. Quick comparison]

[[
Aspect
--
`Args` (gb)
--
`Args` (gb.args)
==
Options parsing
--
❌ No
--
✅ Yes
==
Types (int/float/bool)
--
❌ All string
--
✅ Typed
==
Default values
--
❌
--
✅
==
Automatic help
--
❌
--
✅
==
Automatic version
--
❌
--
✅
==
Simplicity
--
✅ Very simple
--
⚠️ More code
]]

Follow-Ups:
Re: Documentation of gb.args and gb.highlight - GambasBenoît Minisini <benoit.minisini@xxxxxxxxxxxxxxxx>