cmd2 4.0.0b3__tar.gz → 4.0.0b4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/PKG-INFO +1 -1
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/completion.py +34 -15
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2.egg-info/PKG-INFO +1 -1
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_completion.py +139 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/.pre-commit-config.yaml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/.prettierignore +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/.prettierrc +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/.typos.toml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/CHANGELOG.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/GEMINI.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/LICENSE +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/MANIFEST.in +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/Makefile +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/README.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/__init__.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/argparse_completer.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/argparse_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/clipboard.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/cmd2.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/colors.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/command_set.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/constants.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/decorators.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/exceptions.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/history.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/parsing.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/plugin.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/pt_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/py.typed +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/py_bridge.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/rich_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/string_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/styles.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/types.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2/utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2.egg-info/SOURCES.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2.egg-info/dependency_links.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2.egg-info/requires.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2.egg-info/top_level.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/cmd2.png +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/codecov.yml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/argparse_completer.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/argparse_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/clipboard.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/cmd.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/colors.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/command_set.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/completion.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/constants.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/decorators.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/exceptions.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/history.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/parsing.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/plugin.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/pt_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/py_bridge.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/rich_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/string_utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/styles.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/api/utils.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/doc_conventions.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/examples/alternate_event_loops.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/examples/examples.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/examples/getting_started.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/examples/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/argument_processing.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/async_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/builtin_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/clipboard.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/completion.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/disable_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/embedded_python_shells.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/generating_output.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/help.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/history.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/hooks.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/initialization.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/misc.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/modular_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/multiline_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/os.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/packaging.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/plugins.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/prompt.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/redirection.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/scripting.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/settings.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/shortcuts_aliases_macros.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/startup_commands.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/table_creation.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/features/theme.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/javascripts/readthedocs.js +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/migrating/incompatibilities.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/migrating/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/migrating/minimum.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/migrating/next_steps.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/migrating/why.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/mixins/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/mixins/mixin_template.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/overrides/main.html +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/overview/alternatives.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/overview/index.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/overview/installation.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/overview/integrating.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/overview/resources.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/stylesheets/cmd2.css +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/testing.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/docs/upgrades.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/.cmd2rc +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/README.md +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/argparse_completion.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/argparse_example.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/async_call.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/async_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/async_printing.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/basic_completion.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/cmd_as_argument.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/color.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/command_sets.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/custom_parser.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/custom_types.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/default_categories.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/dynamic_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/environment.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/event_loops.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/exit_code.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/getting_started.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/hello_cmd2.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/help_categories.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/hooks.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/migrating.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/mixin.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/modular_commands/__init__.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/modular_commands/commandset_basic.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/modular_commands/commandset_complex.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/modular_commands/commandset_custominit.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/modular_commandsets.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/paged_output.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/persistent_history.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/pretty_print.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/python_scripting.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/read_input.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/remove_builtin_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/remove_settable.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/rich_tables.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/rich_theme.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/scripts/arg_printer.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/scripts/conditional.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/scripts/nested.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/scripts/quit.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/scripts/save_help_text.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/scripts/script.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/scripts/script.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/tmux_launch.sh +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/tmux_split.sh +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/examples/unicode_commands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/mkdocs.yml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/package.json +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/pyproject.toml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/ruff.toml +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/scripts/validate_tag.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/setup.cfg +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/.cmd2rc +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/__init__.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/conftest.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/echo.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/environment.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/help.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/py_locals.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/pyscript_dir.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/raises_exception.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/recursive.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/self_in_py.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/stdout_capture.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/pyscript/stop.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/relative_multiple.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/script.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/script.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/binary.bin +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/empty.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/help.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/nested.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/one_down.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/postcmds.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/precmds.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/scripts/utf8.txt +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_argparse.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_argparse_completer.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_argparse_subcommands.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_argparse_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_categories.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_cmd2.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_commandset.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_dynamic_complete_style.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_future_annotations.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_history.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_parsing.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_plugin.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_pt_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_py_completion.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_rich_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_run_pyscript.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_string_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_utils.py +0 -0
- {cmd2-4.0.0b3 → cmd2-4.0.0b4}/tests/test_utils_defining_class.py +0 -0
|
@@ -31,6 +31,15 @@ from rich.protocol import is_renderable
|
|
|
31
31
|
from . import rich_utils as ru
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
class _UnsetStr(str):
|
|
35
|
+
"""Internal sentinel to distinguish between an unset and an explicit empty string."""
|
|
36
|
+
|
|
37
|
+
__slots__ = ()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_UNSET_STR = _UnsetStr("")
|
|
41
|
+
|
|
42
|
+
|
|
34
43
|
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
35
44
|
class CompletionItem:
|
|
36
45
|
"""A single completion result."""
|
|
@@ -39,17 +48,20 @@ class CompletionItem:
|
|
|
39
48
|
# control sequences (like ^J or ^I) in the completion menu.
|
|
40
49
|
_CONTROL_WHITESPACE_RE = re.compile(r"\r\n|[\n\r\t\f\v]")
|
|
41
50
|
|
|
42
|
-
# The
|
|
43
|
-
# This
|
|
51
|
+
# The core object this completion represents (e.g., str, int, Path).
|
|
52
|
+
# This serves as the default source for the completion string and is used
|
|
53
|
+
# to support object-based validation when used in argparse choices.
|
|
44
54
|
value: Any = field(kw_only=False)
|
|
45
55
|
|
|
46
|
-
# The actual string
|
|
47
|
-
#
|
|
48
|
-
|
|
56
|
+
# The actual completion string. If not provided, defaults to str(value).
|
|
57
|
+
# This can be used to provide a human-friendly alias for complex objects in
|
|
58
|
+
# an argparse choices list (requires a matching 'type' converter for validation).
|
|
59
|
+
text: str = _UNSET_STR
|
|
49
60
|
|
|
50
61
|
# Optional string for displaying the completion differently in the completion menu.
|
|
51
62
|
# This can contain ANSI style sequences. A plain version is stored in display_plain.
|
|
52
|
-
|
|
63
|
+
# If not provided, defaults to the (possibly computed) value of 'text'.
|
|
64
|
+
display: str = _UNSET_STR
|
|
53
65
|
|
|
54
66
|
# Optional meta information about completion which displays in the completion menu.
|
|
55
67
|
# This can contain ANSI style sequences. A plain version is stored in display_meta_plain.
|
|
@@ -60,9 +72,8 @@ class CompletionItem:
|
|
|
60
72
|
table_data: Sequence[Any] = field(default_factory=tuple)
|
|
61
73
|
|
|
62
74
|
# Plain text versions of display fields (stripped of ANSI) for sorting/filtering.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
display_meta_plain: str = field(init=False)
|
|
75
|
+
display_plain: str = field(default="", init=False)
|
|
76
|
+
display_meta_plain: str = field(default="", init=False)
|
|
66
77
|
|
|
67
78
|
@classmethod
|
|
68
79
|
def _clean_display(cls, val: str) -> str:
|
|
@@ -77,13 +88,21 @@ class CompletionItem:
|
|
|
77
88
|
return cls._CONTROL_WHITESPACE_RE.sub(" ", val)
|
|
78
89
|
|
|
79
90
|
def __post_init__(self) -> None:
|
|
80
|
-
"""Finalize the object after initialization.
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
"""Finalize the object after initialization.
|
|
92
|
+
|
|
93
|
+
By using the sentinel pattern to distinguish between a field that was never
|
|
94
|
+
set and one explicitly blanked out, this handles the two-stage lifecycle:
|
|
95
|
+
|
|
96
|
+
1. Initial creation (usually by a developer-provided choices_provider or completer).
|
|
97
|
+
2. Post-processing by cmd2 via dataclasses.replace(), which may modify fields or
|
|
98
|
+
explicitly set them to empty strings.
|
|
99
|
+
"""
|
|
100
|
+
# If the completion string was not provided, derive it from value.
|
|
101
|
+
if isinstance(self.text, _UnsetStr):
|
|
83
102
|
object.__setattr__(self, "text", str(self.value))
|
|
84
103
|
|
|
85
|
-
#
|
|
86
|
-
if
|
|
104
|
+
# If the display string was not provided, use text.
|
|
105
|
+
if isinstance(self.display, _UnsetStr):
|
|
87
106
|
object.__setattr__(self, "display", self.text)
|
|
88
107
|
|
|
89
108
|
# Clean display and display_meta
|
|
@@ -163,7 +182,7 @@ class CompletionResultsBase:
|
|
|
163
182
|
|
|
164
183
|
# True if every item in this collection has a numeric display string.
|
|
165
184
|
# Used for sorting and alignment.
|
|
166
|
-
numeric_display: bool = field(init=False)
|
|
185
|
+
numeric_display: bool = field(default=False, init=False)
|
|
167
186
|
|
|
168
187
|
def __post_init__(self) -> None:
|
|
169
188
|
"""Finalize the object after initialization."""
|
|
@@ -4,6 +4,7 @@ These are primarily tests related to prompt-toolkit completer functions which ha
|
|
|
4
4
|
file system paths, and shell commands.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import argparse
|
|
7
8
|
import dataclasses
|
|
8
9
|
import enum
|
|
9
10
|
import os
|
|
@@ -15,6 +16,7 @@ import pytest
|
|
|
15
16
|
|
|
16
17
|
import cmd2
|
|
17
18
|
from cmd2 import (
|
|
19
|
+
Choices,
|
|
18
20
|
CompletionItem,
|
|
19
21
|
Completions,
|
|
20
22
|
utils,
|
|
@@ -1300,3 +1302,140 @@ def test_subcommand_tab_completion_with_no_completer_scu(scu_app) -> None:
|
|
|
1300
1302
|
|
|
1301
1303
|
completions = scu_app.complete(text, line, begidx, endidx)
|
|
1302
1304
|
assert not completions
|
|
1305
|
+
|
|
1306
|
+
|
|
1307
|
+
def test_set_completion_item_text() -> None:
|
|
1308
|
+
"""Test setting CompletionItem.text and how it affects CompletionItem.display."""
|
|
1309
|
+
value = 5
|
|
1310
|
+
|
|
1311
|
+
# Don't provide text
|
|
1312
|
+
item = CompletionItem(value=value)
|
|
1313
|
+
assert item.text == str(value)
|
|
1314
|
+
|
|
1315
|
+
# Provide text
|
|
1316
|
+
item = CompletionItem(value=value, text="my_text")
|
|
1317
|
+
assert item.text == "my_text"
|
|
1318
|
+
|
|
1319
|
+
# Provide blank text
|
|
1320
|
+
item = CompletionItem(value=value, text="")
|
|
1321
|
+
assert item.text == ""
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
def test_replace_completion_item_text() -> None:
|
|
1325
|
+
"""Test replacing the value of CompletionItem.text"""
|
|
1326
|
+
value = 5
|
|
1327
|
+
|
|
1328
|
+
# Replace text value
|
|
1329
|
+
item = CompletionItem(value=value, text="my_text")
|
|
1330
|
+
updated_item = dataclasses.replace(item, text="new_text")
|
|
1331
|
+
assert item.text == "my_text"
|
|
1332
|
+
assert item.display == "my_text"
|
|
1333
|
+
|
|
1334
|
+
# Text should be updated and display should be the same
|
|
1335
|
+
assert updated_item.text == "new_text"
|
|
1336
|
+
assert updated_item.display == "my_text"
|
|
1337
|
+
|
|
1338
|
+
# Replace text value with blank
|
|
1339
|
+
item = CompletionItem(value=value, text="my_text")
|
|
1340
|
+
updated_item = dataclasses.replace(item, text="")
|
|
1341
|
+
assert item.text == "my_text"
|
|
1342
|
+
assert item.display == "my_text"
|
|
1343
|
+
|
|
1344
|
+
# Text should be updated and display should be the same
|
|
1345
|
+
assert updated_item.text == ""
|
|
1346
|
+
assert updated_item.display == "my_text"
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
def test_set_completion_item_display() -> None:
|
|
1350
|
+
"""Test setting CompletionItem.display and how it is affected by CompletionItem.text."""
|
|
1351
|
+
value = 5
|
|
1352
|
+
|
|
1353
|
+
# Don't provide text or display
|
|
1354
|
+
value = 5
|
|
1355
|
+
item = CompletionItem(value=value)
|
|
1356
|
+
assert item.text == str(value)
|
|
1357
|
+
assert item.display == item.text
|
|
1358
|
+
|
|
1359
|
+
# Don't provide display but provide text
|
|
1360
|
+
item = CompletionItem(value=value, text="my_text")
|
|
1361
|
+
assert item.text == "my_text"
|
|
1362
|
+
assert item.display == item.text
|
|
1363
|
+
|
|
1364
|
+
# Provide display
|
|
1365
|
+
item = CompletionItem(value=value, text="my_text", display="my_display")
|
|
1366
|
+
assert item.text == "my_text"
|
|
1367
|
+
assert item.display == "my_display"
|
|
1368
|
+
|
|
1369
|
+
# Provide blank display
|
|
1370
|
+
item = CompletionItem(value=value, text="my_text", display="")
|
|
1371
|
+
assert item.text == "my_text"
|
|
1372
|
+
assert item.display == ""
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
def test_replace_completion_item_display() -> None:
|
|
1376
|
+
"""Test replacing the value of CompletionItem.display"""
|
|
1377
|
+
value = 5
|
|
1378
|
+
|
|
1379
|
+
# Replace display value
|
|
1380
|
+
item = CompletionItem(value=value, display="my_display")
|
|
1381
|
+
updated_item = dataclasses.replace(item, display="new_display")
|
|
1382
|
+
|
|
1383
|
+
assert item.display == "my_display"
|
|
1384
|
+
assert updated_item.display == "new_display"
|
|
1385
|
+
|
|
1386
|
+
# Replace display value with blank
|
|
1387
|
+
item = CompletionItem(value=value, display="my_display")
|
|
1388
|
+
updated_item = dataclasses.replace(item, display="")
|
|
1389
|
+
|
|
1390
|
+
assert item.display == "my_display"
|
|
1391
|
+
assert updated_item.display == ""
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
def test_full_prefix_removal() -> None:
|
|
1395
|
+
"""Verify that Cmd._perform_completion() can clear item.text when
|
|
1396
|
+
text_to_remove matches item.text exactly. This occurs when completing
|
|
1397
|
+
a nested quoted string where the command line already contains the
|
|
1398
|
+
full unquoted content of the completion match.
|
|
1399
|
+
"""
|
|
1400
|
+
|
|
1401
|
+
class TestApp(cmd2.Cmd):
|
|
1402
|
+
def get_choices(self) -> Choices:
|
|
1403
|
+
"""Return choices."""
|
|
1404
|
+
choices = [
|
|
1405
|
+
"'This is a single-quoted item'",
|
|
1406
|
+
'"This is a double-quoted item"',
|
|
1407
|
+
]
|
|
1408
|
+
return cmd2.Choices.from_values(choices)
|
|
1409
|
+
|
|
1410
|
+
parser = cmd2.Cmd2ArgumentParser()
|
|
1411
|
+
parser.add_argument("arg", choices_provider=get_choices)
|
|
1412
|
+
|
|
1413
|
+
@cmd2.with_argparser(parser)
|
|
1414
|
+
def do_command(self, args: argparse.Namespace) -> None:
|
|
1415
|
+
"""Test stuff."""
|
|
1416
|
+
|
|
1417
|
+
# Test single-quoted item
|
|
1418
|
+
text = ""
|
|
1419
|
+
line = "command \"'This is a single-quoted item'"
|
|
1420
|
+
endidx = len(line)
|
|
1421
|
+
begidx = endidx
|
|
1422
|
+
|
|
1423
|
+
app = TestApp()
|
|
1424
|
+
completions = app.complete(text, line, begidx, endidx)
|
|
1425
|
+
assert len(completions) == 1
|
|
1426
|
+
|
|
1427
|
+
item = completions[0]
|
|
1428
|
+
assert item.text == ""
|
|
1429
|
+
|
|
1430
|
+
# Test double-quoted item
|
|
1431
|
+
text = ""
|
|
1432
|
+
line = 'command \'"This is a double-quoted item"'
|
|
1433
|
+
endidx = len(line)
|
|
1434
|
+
begidx = endidx
|
|
1435
|
+
|
|
1436
|
+
app = TestApp()
|
|
1437
|
+
completions = app.complete(text, line, begidx, endidx)
|
|
1438
|
+
assert len(completions) == 1
|
|
1439
|
+
|
|
1440
|
+
item = completions[0]
|
|
1441
|
+
assert item.text == ""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|