Skip to content

Commit aa992c5

Browse files
committed
FEATURE: command line autocompletion for bash and zsh shells
1 parent 0e1d068 commit aa992c5

18 files changed

+119
-53
lines changed

ARCHITECTURE.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ the following system design requirements were derived:
123123
- [DevOps](https://en.wikipedia.org/wiki/DevOps)
124124
- [CI/CD automation](https://en.wikipedia.org/wiki/CI/CD)
125125
- git pre-commit hooks for code linting and other code quality checks
126+
- create command-line autocompletion for bash, zsh and powershell [PR #134](https://github.com/ArduPilot/MethodicConfigurator/pull/134)
126127

127128
### The Software architecture
128129

@@ -457,3 +458,32 @@ or create a gitlab Pull request with the changes to the `.po` file.
457458
The github [robot will automatically convert that `.po` file into a `.mo` file](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/update_mo_files.yml)
458459
and create an [*ArduPilot methodic configurator* installer](https://github.com/ArduPilot/MethodicConfigurator/actions/workflows/windows_build.yml)
459460
that you can use to test the translations.
461+
462+
## Install command line completion
463+
464+
For Bash autocompletion, add this to your `~/.bashrc`:
465+
466+
```bash
467+
eval "$(register-python-argcomplete ardupilot_methodic_configurator)"
468+
```
469+
470+
For Zsh autocompletion, add these lines to your `~/.zshrc`:
471+
472+
```zsh
473+
autoload -U bashcompinit
474+
bashcompinit
475+
eval "$(register-python-argcomplete ardupilot_methodic_configurator)"
476+
```
477+
478+
For PowerShell autocompletion, run this command in PowerShell:
479+
480+
```powershell
481+
Register-ArgumentCompleter -Native -CommandName ardupilot_methodic_configurator -ScriptBlock {
482+
param($wordToComplete, $commandAst, $cursorPosition)
483+
$env:COMP_LINE=$commandAst.ToString()
484+
$env:COMP_POINT=$cursorPosition
485+
ardupilot_methodic_configurator | ForEach-Object {
486+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
487+
}
488+
}
489+
```

ardupilot_methodic_configurator/__main__.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
from typing import Union
2121
from webbrowser import open as webbrowser_open
2222

23+
import argcomplete
24+
2325
from ardupilot_methodic_configurator import _, __version__
2426
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
2527
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
2628
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
27-
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
29+
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
2830
from ardupilot_methodic_configurator.frontend_tkinter_base import show_error_message
2931
from ardupilot_methodic_configurator.frontend_tkinter_component_editor import ComponentEditorWindow
3032
from ardupilot_methodic_configurator.frontend_tkinter_connection_selection import ConnectionSelectionWindow
@@ -34,14 +36,12 @@
3436
from ardupilot_methodic_configurator.middleware_software_updates import UpdateManager, check_for_software_updates
3537

3638

37-
def argument_parser() -> argparse.Namespace:
39+
def create_argument_parser() -> argparse.ArgumentParser:
3840
"""
39-
Parses command-line arguments for the script.
40-
4141
This function sets up an argument parser to handle the command-line arguments for the script.
4242
4343
Returns:
44-
argparse.Namespace: An object containing the parsed arguments.
44+
argparse.ArgumentParser: The argument parser object.
4545
4646
"""
4747
parser = argparse.ArgumentParser(
@@ -63,7 +63,10 @@ def argument_parser() -> argparse.Namespace:
6363
parser = ComponentEditorWindow.add_argparse_arguments(parser)
6464
parser = ParameterEditorWindow.add_argparse_arguments(parser)
6565
parser = UpdateManager.add_argparse_arguments(parser)
66-
return add_common_arguments_and_parse(parser)
66+
parser = add_common_arguments(parser)
67+
68+
argcomplete.autocomplete(parser)
69+
return parser
6770

6871

6972
def connect_to_fc_and_set_vehicle_type(args: argparse.Namespace) -> tuple[FlightController, str]:
@@ -134,7 +137,7 @@ def component_editor(
134137

135138

136139
def main() -> None:
137-
args = argument_parser()
140+
args = create_argument_parser().parse_args()
138141

139142
logging_basicConfig(level=logging_getLevelName(args.loglevel), format="%(asctime)s - %(levelname)s - %(message)s")
140143

ardupilot_methodic_configurator/annotate_params.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from typing import Any, Optional, Union
3737
from xml.etree import ElementTree as ET # no parsing, just data-structure manipulation
3838

39+
import argcomplete
3940
from defusedxml import ElementTree as DET # noqa: N814, just parsing, no data-structure manipulation
4041

4142
# URL of the XML file
@@ -52,7 +53,7 @@
5253
# mypy: disable-error-code="unused-ignore"
5354

5455

55-
def arg_parser() -> argparse.Namespace:
56+
def create_argument_parser() -> argparse.ArgumentParser:
5657
parser = argparse.ArgumentParser(
5758
description="Fetches on-line ArduPilot parameter documentation and adds it to the "
5859
"specified file or to all *.param and *.parm files in the specified directory."
@@ -107,6 +108,13 @@ def arg_parser() -> argparse.Namespace:
107108
help="Display version information and exit.",
108109
)
109110

111+
argcomplete.autocomplete(parser)
112+
return parser
113+
114+
115+
def parse_arguments() -> argparse.Namespace:
116+
parser = create_argument_parser()
117+
110118
args = parser.parse_args()
111119

112120
if args.verbose:
@@ -818,7 +826,7 @@ def parse_parameter_metadata(
818826

819827

820828
def main() -> None:
821-
args = arg_parser()
829+
args = parse_arguments()
822830
try:
823831
xml_url = get_xml_url(args.vehicle_type, args.firmware_version)
824832
xml_dir = get_xml_dir(args.target)

ardupilot_methodic_configurator/backend_mavftp.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
import struct
1919
import sys
2020
import time
21-
from argparse import ArgumentParser, Namespace
21+
from argparse import ArgumentParser
2222
from collections.abc import Generator
2323
from datetime import datetime
2424
from io import BufferedReader, BufferedWriter
2525
from io import BytesIO as SIO # noqa: N814
2626
from typing import Union
2727

28+
import argcomplete
2829
from pymavlink import mavutil
2930

3031
# pylint: disable=too-many-lines
@@ -1397,7 +1398,7 @@ def decode_and_save_params(fh) -> MAVFTPReturn:
13971398

13981399
if __name__ == "__main__":
13991400

1400-
def argument_parser() -> Namespace:
1401+
def create_argument_parser() -> ArgumentParser:
14011402
"""
14021403
Parses command-line arguments for the script.
14031404
@@ -1527,7 +1528,8 @@ def argument_parser() -> Namespace:
15271528
parser_crc.add_argument("arg1", type=str, metavar="remote_path", help="Path to the file to calculate the CRC of.")
15281529

15291530
# Add other subparsers commands as needed
1530-
return parser.parse_args()
1531+
argcomplete.autocomplete(parser)
1532+
return parser
15311533

15321534
def auto_detect_serial() -> list[mavutil.SerialPort]:
15331535
preferred_ports = [
@@ -1592,7 +1594,7 @@ def wait_heartbeat(m) -> None:
15921594

15931595
def main() -> None:
15941596
"""For testing/example purposes only."""
1595-
args = argument_parser()
1597+
args = create_argument_parser().parse_args()
15961598

15971599
logging.basicConfig(level=logging.getLevelName(args.loglevel), format="%(levelname)s - %(message)s")
15981600

ardupilot_methodic_configurator/common_arguments.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
SPDX-License-Identifier: GPL-3.0-or-later
99
"""
1010

11-
from argparse import ArgumentParser, Namespace
11+
from argparse import ArgumentParser
1212

1313
from ardupilot_methodic_configurator import _, __version__
1414
from ardupilot_methodic_configurator.internationalization import LANGUAGE_CHOICES
1515

1616

17-
def add_common_arguments_and_parse(parser: ArgumentParser) -> Namespace:
17+
def add_common_arguments(parser: ArgumentParser) -> ArgumentParser:
1818
parser.add_argument(
1919
"--loglevel",
2020
type=str,
@@ -32,4 +32,4 @@ def add_common_arguments_and_parse(parser: ArgumentParser) -> Namespace:
3232
choices=LANGUAGE_CHOICES,
3333
help=_("User interface language. Default is %(default)s."),
3434
)
35-
return parser.parse_args()
35+
return parser

ardupilot_methodic_configurator/extract_param_defaults.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import re
1818
from typing import Union
1919

20+
import argcomplete
2021
from pymavlink import mavutil
2122

2223
NO_DEFAULT_VALUES_MESSAGE = "The .bin file contained no parameter default values. Update to a newer ArduPilot firmware version"
@@ -27,17 +28,7 @@
2728
MAV_PARAM_TYPE_REAL32 = 9
2829

2930

30-
def parse_arguments(args: Union[None, argparse.Namespace] = None) -> argparse.Namespace:
31-
"""
32-
Parses command line arguments for the script.
33-
34-
Args:
35-
args: List of command line arguments. Default is None, which means it uses sys.argv.
36-
37-
Returns:
38-
Namespace object containing the parsed arguments.
39-
40-
"""
31+
def create_argument_parser() -> argparse.ArgumentParser:
4132
parser = argparse.ArgumentParser(description="Extracts parameter default values from an ArduPilot .bin log file.")
4233
parser.add_argument(
4334
"-f",
@@ -82,6 +73,23 @@ def parse_arguments(args: Union[None, argparse.Namespace] = None) -> argparse.Na
8273
help="Type of parameter values to extract. Default is %(default)s.",
8374
)
8475
parser.add_argument("bin_file", help="The ArduPilot .bin log file to read")
76+
77+
argcomplete.autocomplete(parser)
78+
return parser
79+
80+
81+
def parse_arguments(args: Union[None, argparse.Namespace] = None) -> argparse.Namespace:
82+
"""
83+
Parses command line arguments for the script.
84+
85+
Args:
86+
args: List of command line arguments. Default is None, which means it uses sys.argv.
87+
88+
Returns:
89+
Namespace object containing the parsed arguments.
90+
91+
"""
92+
parser = create_argument_parser()
8593
args, _ = parser.parse_known_args(args) # type: ignore[arg-type]
8694

8795
if args is None:

ardupilot_methodic_configurator/frontend_tkinter_component_editor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
2727
from ardupilot_methodic_configurator.backend_filesystem_vehicle_components import VehicleComponents
2828
from ardupilot_methodic_configurator.battery_cell_voltages import BatteryCell
29-
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
29+
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
3030

3131
# from ardupilot_methodic_configurator.frontend_tkinter_base import show_tooltip
3232
from ardupilot_methodic_configurator.frontend_tkinter_base import show_error_message
@@ -52,7 +52,7 @@ def argument_parser() -> Namespace:
5252
)
5353
parser = LocalFilesystem.add_argparse_arguments(parser)
5454
parser = ComponentEditorWindow.add_argparse_arguments(parser)
55-
return add_common_arguments_and_parse(parser)
55+
return add_common_arguments(parser).parse_args()
5656
# pylint: enable=duplicate-code
5757

5858

ardupilot_methodic_configurator/frontend_tkinter_component_editor_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from ardupilot_methodic_configurator import _, __version__
2424
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
25-
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
25+
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
2626
from ardupilot_methodic_configurator.frontend_tkinter_base import (
2727
BaseWindow,
2828
RichText,
@@ -52,7 +52,7 @@ def argument_parser() -> Namespace:
5252
)
5353
parser = LocalFilesystem.add_argparse_arguments(parser)
5454
parser = ComponentEditorWindowBase.add_argparse_arguments(parser)
55-
return add_common_arguments_and_parse(parser)
55+
return add_common_arguments(parser).parse_args()
5656
# pylint: enable=duplicate-code
5757

5858

ardupilot_methodic_configurator/frontend_tkinter_connection_selection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from ardupilot_methodic_configurator import _
2424
from ardupilot_methodic_configurator.backend_flightcontroller import FlightController
25-
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
25+
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
2626
from ardupilot_methodic_configurator.frontend_tkinter_base import (
2727
BaseWindow,
2828
ProgressWindow,
@@ -274,7 +274,7 @@ def argument_parser() -> Namespace:
274274
)
275275
)
276276
parser = FlightController.add_argparse_arguments(parser)
277-
return add_common_arguments_and_parse(parser)
277+
return add_common_arguments(parser).parse_args()
278278

279279

280280
# pylint: disable=duplicate-code

ardupilot_methodic_configurator/frontend_tkinter_directory_selection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from ardupilot_methodic_configurator import _, __version__
2525
from ardupilot_methodic_configurator.backend_filesystem import LocalFilesystem
2626
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
27-
from ardupilot_methodic_configurator.common_arguments import add_common_arguments_and_parse
27+
from ardupilot_methodic_configurator.common_arguments import add_common_arguments
2828
from ardupilot_methodic_configurator.frontend_tkinter_base import BaseWindow, show_no_param_files_error, show_tooltip
2929
from ardupilot_methodic_configurator.frontend_tkinter_template_overview import TemplateOverviewWindow
3030

@@ -491,7 +491,7 @@ def argument_parser() -> Namespace:
491491
)
492492
)
493493
parser = LocalFilesystem.add_argparse_arguments(parser)
494-
return add_common_arguments_and_parse(parser)
494+
return add_common_arguments(parser).parse_args()
495495

496496

497497
# pylint: disable=duplicate-code

0 commit comments

Comments
 (0)