mycli 2.2.0__tar.gz → 2.3.0__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.
Files changed (174) hide show
  1. {mycli-2.2.0 → mycli-2.3.0}/AGENTS.md +2 -2
  2. {mycli-2.2.0/mycli.egg-info → mycli-2.3.0}/PKG-INFO +30 -13
  3. {mycli-2.2.0 → mycli-2.3.0}/README.md +29 -12
  4. {mycli-2.2.0 → mycli-2.3.0}/changelog.md +17 -0
  5. {mycli-2.2.0 → mycli-2.3.0}/mycli/TIPS +7 -1
  6. {mycli-2.2.0 → mycli-2.3.0}/mycli/cli_runner.py +22 -0
  7. {mycli-2.2.0 → mycli-2.3.0}/mycli/clibuffer.py +1 -0
  8. {mycli-2.2.0 → mycli-2.3.0}/mycli/client.py +10 -1
  9. {mycli-2.2.0 → mycli-2.3.0}/mycli/client_connection.py +28 -1
  10. {mycli-2.2.0 → mycli-2.3.0}/mycli/client_query.py +2 -0
  11. {mycli-2.2.0 → mycli-2.3.0}/mycli/completion_refresher.py +56 -21
  12. {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/execute.py +3 -3
  13. {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/repl.py +17 -0
  14. {mycli-2.2.0 → mycli-2.3.0}/mycli/myclirc +28 -1
  15. {mycli-2.2.0 → mycli-2.3.0}/mycli/output.py +34 -5
  16. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/__init__.py +4 -0
  17. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/iocommands.py +11 -1
  18. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/main.py +9 -1
  19. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/utils.py +21 -6
  20. {mycli-2.2.0 → mycli-2.3.0}/mycli/schema_prefetcher.py +27 -3
  21. {mycli-2.2.0 → mycli-2.3.0}/mycli/sqlexecute.py +6 -0
  22. {mycli-2.2.0 → mycli-2.3.0/mycli.egg-info}/PKG-INFO +30 -13
  23. {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/scm_file_list.json +129 -129
  24. mycli-2.3.0/mycli.egg-info/scm_version.json +8 -0
  25. {mycli-2.2.0 → mycli-2.3.0}/test/features/fixture_data/help_commands.txt +8 -7
  26. {mycli-2.2.0 → mycli-2.3.0}/test/myclirc +27 -0
  27. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_cli_runner.py +201 -0
  28. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client.py +10 -0
  29. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client_connection.py +66 -0
  30. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_completion_refresher.py +133 -1
  31. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_repl.py +47 -1
  32. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_output.py +102 -0
  33. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_schema_prefetcher.py +140 -0
  34. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_utils.py +20 -0
  35. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sqlexecute.py +12 -5
  36. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_tabular_output.py +5 -0
  37. {mycli-2.2.0 → mycli-2.3.0}/test/utils.py +1 -0
  38. mycli-2.2.0/mycli.egg-info/scm_version.json +0 -8
  39. {mycli-2.2.0 → mycli-2.3.0}/.git-blame-ignore-revs +0 -0
  40. {mycli-2.2.0 → mycli-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  41. {mycli-2.2.0 → mycli-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  42. {mycli-2.2.0 → mycli-2.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  43. {mycli-2.2.0 → mycli-2.3.0}/.github/dependabot.yml +0 -0
  44. {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/ci.yml +0 -0
  45. {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/lint.yml +0 -0
  46. {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/publish.yml +0 -0
  47. {mycli-2.2.0 → mycli-2.3.0}/.github/workflows/typecheck.yml +0 -0
  48. {mycli-2.2.0 → mycli-2.3.0}/.gitignore +0 -0
  49. {mycli-2.2.0 → mycli-2.3.0}/AUTHORS.rst +0 -0
  50. {mycli-2.2.0 → mycli-2.3.0}/CONTRIBUTING.md +0 -0
  51. {mycli-2.2.0 → mycli-2.3.0}/LICENSE.txt +0 -0
  52. {mycli-2.2.0 → mycli-2.3.0}/MANIFEST.in +0 -0
  53. {mycli-2.2.0 → mycli-2.3.0}/SPONSORS.rst +0 -0
  54. {mycli-2.2.0 → mycli-2.3.0}/doc/key_bindings.rst +0 -0
  55. {mycli-2.2.0 → mycli-2.3.0}/doc/llm.md +0 -0
  56. {mycli-2.2.0 → mycli-2.3.0}/doc/screenshots/main.gif +0 -0
  57. {mycli-2.2.0 → mycli-2.3.0}/doc/screenshots/tables.png +0 -0
  58. {mycli-2.2.0 → mycli-2.3.0}/mycli/AUTHORS +0 -0
  59. {mycli-2.2.0 → mycli-2.3.0}/mycli/SPONSORS +0 -0
  60. {mycli-2.2.0 → mycli-2.3.0}/mycli/__init__.py +0 -0
  61. {mycli-2.2.0 → mycli-2.3.0}/mycli/app_state.py +0 -0
  62. {mycli-2.2.0 → mycli-2.3.0}/mycli/client_commands.py +0 -0
  63. {mycli-2.2.0 → mycli-2.3.0}/mycli/clistyle.py +0 -0
  64. {mycli-2.2.0 → mycli-2.3.0}/mycli/clitoolbar.py +0 -0
  65. {mycli-2.2.0 → mycli-2.3.0}/mycli/compat.py +0 -0
  66. {mycli-2.2.0 → mycli-2.3.0}/mycli/config.py +0 -0
  67. {mycli-2.2.0 → mycli-2.3.0}/mycli/constants.py +0 -0
  68. {mycli-2.2.0 → mycli-2.3.0}/mycli/key_bindings.py +0 -0
  69. {mycli-2.2.0 → mycli-2.3.0}/mycli/lexer.py +0 -0
  70. {mycli-2.2.0 → mycli-2.3.0}/mycli/main.py +0 -0
  71. {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/batch.py +0 -0
  72. {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/checkup.py +0 -0
  73. {mycli-2.2.0 → mycli-2.3.0}/mycli/main_modes/list_dsn.py +0 -0
  74. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/__init__.py +0 -0
  75. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/batch_utils.py +0 -0
  76. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/cli_utils.py +0 -0
  77. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/completion_engine.py +0 -0
  78. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/filepaths.py +0 -0
  79. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/hybrid_redirection.py +0 -0
  80. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/interactive_utils.py +0 -0
  81. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/key_binding_utils.py +0 -0
  82. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/__init__.py +0 -0
  83. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/fzf.py +0 -0
  84. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/history.py +0 -0
  85. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/ptoolkit/utils.py +0 -0
  86. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/dbcommands.py +0 -0
  87. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/delimitercommand.py +0 -0
  88. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/dsn_aliases.py +0 -0
  89. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/favoritequeries.py +0 -0
  90. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/special/llm.py +0 -0
  91. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/sql_utils.py +0 -0
  92. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/sqlresult.py +0 -0
  93. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/string_utils.py +0 -0
  94. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/tabular_output/__init__.py +0 -0
  95. {mycli-2.2.0 → mycli-2.3.0}/mycli/packages/tabular_output/sql_format.py +0 -0
  96. {mycli-2.2.0 → mycli-2.3.0}/mycli/sqlcompleter.py +0 -0
  97. {mycli-2.2.0 → mycli-2.3.0}/mycli/ssh_tunnel.py +0 -0
  98. {mycli-2.2.0 → mycli-2.3.0}/mycli/types.py +0 -0
  99. {mycli-2.2.0 → mycli-2.3.0}/mycli/vault.py +0 -0
  100. {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/SOURCES.txt +0 -0
  101. {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/dependency_links.txt +0 -0
  102. {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/entry_points.txt +0 -0
  103. {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/requires.txt +0 -0
  104. {mycli-2.2.0 → mycli-2.3.0}/mycli.egg-info/top_level.txt +0 -0
  105. {mycli-2.2.0 → mycli-2.3.0}/pyproject.toml +0 -0
  106. {mycli-2.2.0 → mycli-2.3.0}/setup.cfg +0 -0
  107. {mycli-2.2.0 → mycli-2.3.0}/test/__init__.py +0 -0
  108. {mycli-2.2.0 → mycli-2.3.0}/test/features/__init__.py +0 -0
  109. {mycli-2.2.0 → mycli-2.3.0}/test/features/auto_vertical.feature +0 -0
  110. {mycli-2.2.0 → mycli-2.3.0}/test/features/basic_commands.feature +0 -0
  111. {mycli-2.2.0 → mycli-2.3.0}/test/features/connection.feature +0 -0
  112. {mycli-2.2.0 → mycli-2.3.0}/test/features/crud_database.feature +0 -0
  113. {mycli-2.2.0 → mycli-2.3.0}/test/features/crud_table.feature +0 -0
  114. {mycli-2.2.0 → mycli-2.3.0}/test/features/db_utils.py +0 -0
  115. {mycli-2.2.0 → mycli-2.3.0}/test/features/environment.py +0 -0
  116. {mycli-2.2.0 → mycli-2.3.0}/test/features/fixture_utils.py +0 -0
  117. {mycli-2.2.0 → mycli-2.3.0}/test/features/iocommands.feature +0 -0
  118. {mycli-2.2.0 → mycli-2.3.0}/test/features/named_queries.feature +0 -0
  119. {mycli-2.2.0 → mycli-2.3.0}/test/features/specials.feature +0 -0
  120. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/__init__.py +0 -0
  121. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/auto_vertical.py +0 -0
  122. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/basic_commands.py +0 -0
  123. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/connection.py +0 -0
  124. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/crud_database.py +0 -0
  125. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/crud_table.py +0 -0
  126. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/iocommands.py +0 -0
  127. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/named_queries.py +0 -0
  128. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/specials.py +0 -0
  129. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/utils.py +0 -0
  130. {mycli-2.2.0 → mycli-2.3.0}/test/features/steps/wrappers.py +0 -0
  131. {mycli-2.2.0 → mycli-2.3.0}/test/features/wrappager.py +0 -0
  132. {mycli-2.2.0 → mycli-2.3.0}/test/mylogin.cnf +0 -0
  133. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/conftest.py +0 -0
  134. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_app_state.py +0 -0
  135. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_batch_utils.py +0 -0
  136. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_checkup.py +0 -0
  137. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_cli_utils.py +0 -0
  138. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_clibuffer.py +0 -0
  139. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client_commands.py +0 -0
  140. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_client_query.py +0 -0
  141. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_clistyle.py +0 -0
  142. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_clitoolbar.py +0 -0
  143. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_completer_use_switch.py +0 -0
  144. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_completion_engine.py +0 -0
  145. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_config.py +0 -0
  146. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_delimitercommand.py +0 -0
  147. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_dsn_aliases.py +0 -0
  148. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_favoritequeries.py +0 -0
  149. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_filepaths.py +0 -0
  150. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_hybrid_redirection.py +0 -0
  151. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_interactive_utils.py +0 -0
  152. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_key_binding_utils.py +0 -0
  153. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_key_bindings.py +0 -0
  154. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_lexer.py +0 -0
  155. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main.py +0 -0
  156. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_batch.py +0 -0
  157. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_execute.py +0 -0
  158. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_main_modes_list_dsn.py +0 -0
  159. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_naive_completion.py +0 -0
  160. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ptoolkit_fzf.py +0 -0
  161. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ptoolkit_history.py +0 -0
  162. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ptoolkit_utils.py +0 -0
  163. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_smart_completion_public_schema_only.py +0 -0
  164. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_dbcommands.py +0 -0
  165. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_init.py +0 -0
  166. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_iocommands.py +0 -0
  167. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_llm.py +0 -0
  168. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_special_main.py +0 -0
  169. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sql_utils.py +0 -0
  170. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sqlcompleter.py +0 -0
  171. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_sqlresult.py +0 -0
  172. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_ssh_tunnel.py +0 -0
  173. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_string_utils.py +0 -0
  174. {mycli-2.2.0 → mycli-2.3.0}/test/pytests/test_vault.py +0 -0
@@ -31,7 +31,7 @@ A command line client for MySQL with auto-completion and syntax highlighting.
31
31
  ├── mycli/main_modes/checkup.py # `--checkup` mode
32
32
  ├── mycli/main_modes/execute.py # `--execute` mode
33
33
  ├── mycli/main_modes/list_dsn.py # `--list-dsn` mode
34
- ├── mycli/main_modes/repl.py # interactive REPL made
34
+ ├── mycli/main_modes/repl.py # interactive REPL mode
35
35
  ├── mycli/myclirc # project-level configuration file
36
36
  ├── mycli/output.py # `OutputMixin` mixin for feedback and output of query results
37
37
  ├── mycli/packages/ # application packages
@@ -53,11 +53,11 @@ A command line client for MySQL with auto-completion and syntax highlighting.
53
53
  ├── mycli/sqlexecute.py # runs SQL queries
54
54
  ├── mycli/types.py # shared types
55
55
  ├── mycli/vault.py # Vault integration
56
- ├── test/conftest.py # pytest configuration
57
56
  ├── test/features/ # behave tests
58
57
  ├── test/myclirc # mycli configuration used for tests
59
58
  ├── test/mylogin.cnf # `mylogin.cnf` example used for tests
60
59
  ├── test/pytests/ # pytest tests
60
+ ├── test/pytests/conftest.py # pytest configuration
61
61
  └── test/utils.py # shared utilities for tests
62
62
 
63
63
  ## Development
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycli
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: CLI for MySQL Database. With auto-completion and syntax highlighting.
5
5
  Author: Mycli Core Team
6
6
  License-Expression: BSD-3-Clause
@@ -89,13 +89,13 @@ You might need `sudo` on Linux.
89
89
  pip install --upgrade 'mycli[all]'
90
90
  ```
91
91
 
92
- or, only on macOS (`fzf` and `pygments` are optional):
92
+ or, only on macOS (`fzf` and `pygments` are optional but recommended):
93
93
 
94
94
  ```bash
95
95
  brew update && brew install mycli fzf pygments
96
96
  ```
97
97
 
98
- or, only on Debian or Ubuntu (`fzf` and `pygments` are optional):
98
+ or, only on Debian or Ubuntu (`fzf` and `pygments` are optional but recommended):
99
99
 
100
100
  ```bash
101
101
  sudo apt-get install mycli fzf python3-pygments
@@ -115,6 +115,7 @@ Features
115
115
  * Auto-completion as you type for SQL keywords as well as tables, views,
116
116
  columns, enums, and more!
117
117
  * Fuzzy history search using [fzf](https://github.com/junegunn/fzf).
118
+ * Output explorer using [fzf](https://github.com/junegunn/fzf) or other tools.
118
119
  * Syntax highlighting using [Pygments](https://pygments.org/).
119
120
  * Smart-completion (enabled by default) will suggest context-sensitive completion.
120
121
  - `SELECT * FROM <tab>` will only show table names.
@@ -158,15 +159,15 @@ These are some alternative ways to install mycli that are not managed by our
158
159
  team but provided by OS package maintainers. OS packages could be somewhat
159
160
  out of date.
160
161
 
161
- If present, the `fzf` package can be used for fuzzy history search, and
162
- `pygemtize` can be used for syntax highlighting within the fuzzy history
163
- search. The `less` package is also expected, but almost always already
164
- installed.
162
+ If present, the `fzf` package can be used for fuzzy history search, and as an
163
+ output "explorer" with the `\x` special command. `pygemtize` can be used for
164
+ syntax highlighting within the fuzzy history search. The `less` package is
165
+ also expected, but almost always already installed.
165
166
 
166
167
  ### Arch, Manjaro
167
168
 
168
169
  You can install the `mycli` package available in the AUR. `fzf` and
169
- `python-pygments` are optional:
170
+ `python-pygments` are optional but recommended:
170
171
 
171
172
  ```bash
172
173
  yay -S mycli fzf python-pygments
@@ -175,7 +176,8 @@ yay -S mycli fzf python-pygments
175
176
  ### Debian, Ubuntu
176
177
 
177
178
  On Debian and Ubuntu distributions, you can easily install the mycli package
178
- using apt. The `fzf` and `python3-pygments` packages are optional:
179
+ using apt. The `fzf` and `python3-pygments` packages are optional but
180
+ recommended:
179
181
 
180
182
  ```bash
181
183
  sudo apt-get install mycli fzf python3-pygments
@@ -184,7 +186,7 @@ sudo apt-get install mycli fzf python3-pygments
184
186
  ### Fedora
185
187
 
186
188
  Fedora has a package available for mycli; install it using dnf. The `fzf` and
187
- `python-pygments` packages are optional:
189
+ `python-pygments` packages are optional but recommended:
188
190
 
189
191
  ```bash
190
192
  sudo dnf install mycli fzf python-pygments
@@ -196,7 +198,9 @@ sudo dnf install mycli fzf python-pygments
196
198
 
197
199
  Install the `less` pager, for example by `scoop install less`.
198
200
 
199
- Follow the instructions on this blogpost: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
201
+ Install the `fzf` fuzzy finder, for example by `scoop install fzf`.
202
+
203
+ Follow the instructions on this blog post: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
200
204
 
201
205
  The libraries used in mycli are Windows-compatible, but there are known
202
206
  limitations according to the test suite. The basics work without any
@@ -209,11 +213,24 @@ PRs to address shortcomings on Windows would be welcome!
209
213
  Mycli is more compatible with WSL than with native Windows, though still
210
214
  not 100% perfect. This is a good option for using mycli on Windows.
211
215
 
216
+ With WSL, you are probably using an Ubuntu distribution, in which case
217
+ recommended dependencies can be installed by
218
+
219
+ ```bash
220
+ sudo apt-get install fzf python3-pygments
221
+ ```
222
+
223
+ and mycli can be installed by `apt-get` or by `pip` (recommended):
224
+
225
+ ```bash
226
+ pip install --upgrade 'mycli[all]'
227
+ ```
228
+
212
229
  PRs to complete WSL support would be welcome!
213
230
 
214
231
  ### Thanks
215
232
 
216
- This project was funded through kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
233
+ This project was funded through Kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
217
234
 
218
235
  A special thanks to [Jonathan Slenders](https://twitter.com/jonathan_s) for
219
236
  creating [Python Prompt Toolkit](https://github.com/jonathanslenders/python-prompt-toolkit),
@@ -241,7 +258,7 @@ following in `~/.myclirc`:
241
258
  default_character_set = utf8
242
259
  ```
243
260
 
244
- or set `--charset=utf8` when invoking MyCLI.
261
+ or set `--charset=utf8` when invoking mycli
245
262
 
246
263
  ### Configuration and Usage
247
264
 
@@ -32,13 +32,13 @@ You might need `sudo` on Linux.
32
32
  pip install --upgrade 'mycli[all]'
33
33
  ```
34
34
 
35
- or, only on macOS (`fzf` and `pygments` are optional):
35
+ or, only on macOS (`fzf` and `pygments` are optional but recommended):
36
36
 
37
37
  ```bash
38
38
  brew update && brew install mycli fzf pygments
39
39
  ```
40
40
 
41
- or, only on Debian or Ubuntu (`fzf` and `pygments` are optional):
41
+ or, only on Debian or Ubuntu (`fzf` and `pygments` are optional but recommended):
42
42
 
43
43
  ```bash
44
44
  sudo apt-get install mycli fzf python3-pygments
@@ -58,6 +58,7 @@ Features
58
58
  * Auto-completion as you type for SQL keywords as well as tables, views,
59
59
  columns, enums, and more!
60
60
  * Fuzzy history search using [fzf](https://github.com/junegunn/fzf).
61
+ * Output explorer using [fzf](https://github.com/junegunn/fzf) or other tools.
61
62
  * Syntax highlighting using [Pygments](https://pygments.org/).
62
63
  * Smart-completion (enabled by default) will suggest context-sensitive completion.
63
64
  - `SELECT * FROM <tab>` will only show table names.
@@ -101,15 +102,15 @@ These are some alternative ways to install mycli that are not managed by our
101
102
  team but provided by OS package maintainers. OS packages could be somewhat
102
103
  out of date.
103
104
 
104
- If present, the `fzf` package can be used for fuzzy history search, and
105
- `pygemtize` can be used for syntax highlighting within the fuzzy history
106
- search. The `less` package is also expected, but almost always already
107
- installed.
105
+ If present, the `fzf` package can be used for fuzzy history search, and as an
106
+ output "explorer" with the `\x` special command. `pygemtize` can be used for
107
+ syntax highlighting within the fuzzy history search. The `less` package is
108
+ also expected, but almost always already installed.
108
109
 
109
110
  ### Arch, Manjaro
110
111
 
111
112
  You can install the `mycli` package available in the AUR. `fzf` and
112
- `python-pygments` are optional:
113
+ `python-pygments` are optional but recommended:
113
114
 
114
115
  ```bash
115
116
  yay -S mycli fzf python-pygments
@@ -118,7 +119,8 @@ yay -S mycli fzf python-pygments
118
119
  ### Debian, Ubuntu
119
120
 
120
121
  On Debian and Ubuntu distributions, you can easily install the mycli package
121
- using apt. The `fzf` and `python3-pygments` packages are optional:
122
+ using apt. The `fzf` and `python3-pygments` packages are optional but
123
+ recommended:
122
124
 
123
125
  ```bash
124
126
  sudo apt-get install mycli fzf python3-pygments
@@ -127,7 +129,7 @@ sudo apt-get install mycli fzf python3-pygments
127
129
  ### Fedora
128
130
 
129
131
  Fedora has a package available for mycli; install it using dnf. The `fzf` and
130
- `python-pygments` packages are optional:
132
+ `python-pygments` packages are optional but recommended:
131
133
 
132
134
  ```bash
133
135
  sudo dnf install mycli fzf python-pygments
@@ -139,7 +141,9 @@ sudo dnf install mycli fzf python-pygments
139
141
 
140
142
  Install the `less` pager, for example by `scoop install less`.
141
143
 
142
- Follow the instructions on this blogpost: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
144
+ Install the `fzf` fuzzy finder, for example by `scoop install fzf`.
145
+
146
+ Follow the instructions on this blog post: https://web.archive.org/web/20221006045208/https://www.codewall.co.uk/installing-using-mycli-on-windows/
143
147
 
144
148
  The libraries used in mycli are Windows-compatible, but there are known
145
149
  limitations according to the test suite. The basics work without any
@@ -152,11 +156,24 @@ PRs to address shortcomings on Windows would be welcome!
152
156
  Mycli is more compatible with WSL than with native Windows, though still
153
157
  not 100% perfect. This is a good option for using mycli on Windows.
154
158
 
159
+ With WSL, you are probably using an Ubuntu distribution, in which case
160
+ recommended dependencies can be installed by
161
+
162
+ ```bash
163
+ sudo apt-get install fzf python3-pygments
164
+ ```
165
+
166
+ and mycli can be installed by `apt-get` or by `pip` (recommended):
167
+
168
+ ```bash
169
+ pip install --upgrade 'mycli[all]'
170
+ ```
171
+
155
172
  PRs to complete WSL support would be welcome!
156
173
 
157
174
  ### Thanks
158
175
 
159
- This project was funded through kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
176
+ This project was funded through Kickstarter. Our thanks to the [backers](https://mycli.net/sponsors) who supported the project.
160
177
 
161
178
  A special thanks to [Jonathan Slenders](https://twitter.com/jonathan_s) for
162
179
  creating [Python Prompt Toolkit](https://github.com/jonathanslenders/python-prompt-toolkit),
@@ -184,7 +201,7 @@ following in `~/.myclirc`:
184
201
  default_character_set = utf8
185
202
  ```
186
203
 
187
- or set `--charset=utf8` when invoking MyCLI.
204
+ or set `--charset=utf8` when invoking mycli
188
205
 
189
206
  ### Configuration and Usage
190
207
 
@@ -1,3 +1,20 @@
1
+ 2.3.0 (2026/07/16)
2
+ ==============
3
+
4
+ Features
5
+ ---------
6
+ * Set minimum times for transient toolbar messages.
7
+ * `editor_command` entry in `~/.myclirc` which overrides environment.
8
+ * Experimental: let Vault properties be set in DSNs.
9
+ * Add a `\x` special command to send output to an explorer rather than a pager.
10
+
11
+
12
+ Documentation
13
+ ---------
14
+ * Spellcheck myclirc commentary.
15
+ * Better ordering for `/help` table.
16
+
17
+
1
18
  2.2.0 (2026/07/11)
2
19
  ==============
3
20
 
@@ -138,7 +138,9 @@ set environment variable MYSQL_DSN to set a default DSN!
138
138
  ### general
139
139
  ###
140
140
 
141
- display query output vertically using \G at the end of a query!
141
+ display output vertically using \G at the end of a query!
142
+
143
+ display output in an interactive explorer \x at the end of a query!
142
144
 
143
145
  run SQL scripts in batch mode using the standard input!
144
146
 
@@ -282,6 +284,10 @@ use "min_completion_trigger" in ~/.myclirc to defer completions!
282
284
 
283
285
  colorize search previews with "highlight_preview" in ~/.myclirc!
284
286
 
287
+ set editor_command = "code --wait" in ~/.myclirc to edit queries using VS Code!
288
+
289
+ set up an interactive output explorer using the "[explorer]" section of ~/.myclirc!
290
+
285
291
  ###
286
292
  ### redirection
287
293
  ###
@@ -41,6 +41,11 @@ KNOWN_DSN_QUERY_PARAMS = {
41
41
  'ssl_mode',
42
42
  'ssl_verify_server_cert',
43
43
  'tls_version',
44
+ 'vault_address',
45
+ 'vault_mount',
46
+ 'vault_secret',
47
+ 'vault_password_field',
48
+ 'vault_username_field',
44
49
  }
45
50
 
46
51
 
@@ -277,6 +282,16 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
277
282
  cli_args.character_set = cli_args.character_set or params[0]
278
283
  if params := dsn_params.get('ssh_jump'):
279
284
  cli_args.ssh_jump = cli_args.ssh_jump or params[0]
285
+ if params := dsn_params.get('vault_address'):
286
+ cli_args.vault_address = cli_args.vault_address or params[0]
287
+ if params := dsn_params.get('vault_mount'):
288
+ cli_args.vault_mount = cli_args.vault_mount or params[0]
289
+ if params := dsn_params.get('vault_secret'):
290
+ cli_args.vault_secret = cli_args.vault_secret or params[0]
291
+ if params := dsn_params.get('vault_password_field'):
292
+ cli_args.vault_password_field = cli_args.vault_password_field or params[0]
293
+ if params := dsn_params.get('vault_username_field'):
294
+ cli_args.vault_username_field = cli_args.vault_username_field or params[0]
280
295
 
281
296
  keepalive_ticks = cli_args.keepalive_ticks if cli_args.keepalive_ticks is not None else mycli.default_keepalive_ticks
282
297
  ssl_mode = cli_args.ssl_mode or mycli.ssl_mode
@@ -388,6 +403,7 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
388
403
  sys.exit(1)
389
404
  else:
390
405
  vault_username = None
406
+ vault_username_from_vault = vault_username is not None
391
407
 
392
408
  try:
393
409
  mycli.connect(
@@ -407,6 +423,12 @@ def run_from_cli_args(cli_args: 'CliArgs', client_factory: ClientFactory) -> Non
407
423
  keepalive_ticks=keepalive_ticks,
408
424
  ssh_jump=cli_args.ssh_jump,
409
425
  ssh_cli_options=cli_args.ssh_options,
426
+ vault_address=cli_args.vault_address,
427
+ vault_mount=cli_args.vault_mount,
428
+ vault_secret=cli_args.vault_secret,
429
+ vault_password_field=cli_args.vault_password_field,
430
+ vault_username_field=cli_args.vault_username_field,
431
+ vault_username_from_vault=vault_username_from_vault,
410
432
  )
411
433
 
412
434
  if combined_init_cmd:
@@ -42,6 +42,7 @@ def _multiline_exception(text: str) -> bool:
42
42
  # or ended with certain commands
43
43
  "\\g",
44
44
  "\\G",
45
+ "\\x",
45
46
  r"\e",
46
47
  r"\edit",
47
48
  r"\clip",
@@ -114,10 +114,15 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
114
114
  self.dsn_alias: str | None = None
115
115
  self.main_formatter = TabularOutputFormatter(format_name=c["main"]["table_format"])
116
116
  self.redirect_formatter = TabularOutputFormatter(format_name=c["main"].get("redirect_format", "csv"))
117
+ self.explorer_formatter = TabularOutputFormatter(format_name=c['explorer'].get('format', self.main_formatter))
117
118
  sql_format.register_new_formatter(self.main_formatter)
118
119
  sql_format.register_new_formatter(self.redirect_formatter)
120
+ sql_format.register_new_formatter(self.explorer_formatter)
119
121
  self.main_formatter.mycli = self
120
122
  self.redirect_formatter.mycli = self
123
+ self.explorer_formatter.mycli = self
124
+ self.explorer_command = c['explorer'].get('explorer_command')
125
+ self.explorer_trim_footer = c['explorer'].as_bool('trim_footer')
121
126
  self.syntax_style = c["main"]["syntax_style"]
122
127
  self.verbosity = -1 if c["main"].as_bool("less_chatty") else 0
123
128
  if cli_verbosity:
@@ -155,7 +160,7 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
155
160
  self.echo("Error: Unable to open the audit log file. Your queries will not be logged.", err=True, fg="red")
156
161
  self.logfile = False
157
162
 
158
- self.completion_refresher = CompletionRefresher()
163
+ self.completion_refresher = CompletionRefresher(self._invalidate_prompt_session)
159
164
  self.prefetch_schemas_mode = c["main"].get("prefetch_schemas_mode", "always") or "always"
160
165
  raw_prefetch_list = c["main"].as_list("prefetch_schemas_list") if "prefetch_schemas_list" in c["main"] else []
161
166
  self.prefetch_schemas_list = [s.strip() for s in raw_prefetch_list if s and s.strip()]
@@ -201,6 +206,10 @@ class MyCli(AppStateMixin, OutputMixin, ClientCommandsMixin, ClientConnectionMix
201
206
  self.destructive_keywords = destructive_keywords_from_config(c)
202
207
  special.set_destructive_keywords(self.destructive_keywords)
203
208
 
209
+ def _invalidate_prompt_session(self) -> None:
210
+ if self.prompt_session:
211
+ self.prompt_session.app.invalidate()
212
+
204
213
  def close(self) -> None:
205
214
  try:
206
215
  self.schema_prefetcher.stop()
@@ -63,6 +63,12 @@ class ClientConnectionMixin:
63
63
  keepalive_ticks: int | None = None,
64
64
  ssh_jump: str | None = None,
65
65
  ssh_cli_options: str | None = None,
66
+ vault_address: str | None = None,
67
+ vault_mount: str | None = None,
68
+ vault_secret: str | None = None,
69
+ vault_password_field: str | None = None,
70
+ vault_username_field: str | None = None,
71
+ vault_username_from_vault: bool = False,
66
72
  ) -> None:
67
73
  mylogin_cnf: dict[str, Any] = self.read_mylogin_cnf(self.mylogin_cnf)
68
74
  # Fall back to .mylogin.cnf values only if user did not specify a value.
@@ -191,15 +197,36 @@ class ClientConnectionMixin:
191
197
  assert not isinstance(passwd, int)
192
198
 
193
199
  display_dsn = None
200
+ display_dsn_user = None if vault_username_from_vault else user
194
201
  if self.ssh_tunnel:
195
202
  display_dsn = format_connection_dsn(
196
- user=user,
203
+ user=display_dsn_user,
197
204
  host=remote_host,
198
205
  port=remote_port,
199
206
  socket=remote_socket,
200
207
  database=database,
201
208
  character_set=character_set,
202
209
  ssh_jump=ssh_jump,
210
+ vault_address=vault_address,
211
+ vault_mount=vault_mount,
212
+ vault_secret=vault_secret,
213
+ vault_password_field=vault_password_field,
214
+ vault_username_field=vault_username_field,
215
+ )
216
+ elif vault_secret:
217
+ display_dsn = format_connection_dsn(
218
+ user=display_dsn_user,
219
+ host=host,
220
+ port=int_port,
221
+ socket=socket,
222
+ database=database,
223
+ character_set=character_set,
224
+ ssh_jump=ssh_jump,
225
+ vault_address=vault_address,
226
+ vault_mount=vault_mount,
227
+ vault_secret=vault_secret,
228
+ vault_password_field=vault_password_field,
229
+ vault_username_field=vault_username_field,
203
230
  )
204
231
 
205
232
  connection_info: dict[str, Any] = {
@@ -20,6 +20,7 @@ class ClientQueryMixin:
20
20
  smart_completion: bool
21
21
  main_formatter: Any
22
22
  redirect_formatter: Any
23
+ explorer_formatter: Any
23
24
  prompt_session: Any
24
25
  null_string: str | None
25
26
  numeric_alignment: str | None
@@ -86,6 +87,7 @@ class ClientQueryMixin:
86
87
  for result in results:
87
88
  self.main_formatter.query = query
88
89
  self.redirect_formatter.query = query
90
+ self.explorer_formatter.query = query
89
91
  output = self.format_sqlresult(
90
92
  result,
91
93
  is_expanded=special.is_expanded_output(),
@@ -1,4 +1,5 @@
1
1
  import threading
2
+ from time import monotonic
2
3
  from typing import Callable
3
4
 
4
5
  import pymysql
@@ -8,13 +9,18 @@ from mycli.packages.sqlresult import SQLResult
8
9
  from mycli.sqlcompleter import SQLCompleter
9
10
  from mycli.sqlexecute import ServerSpecies, SQLExecute
10
11
 
12
+ MIN_COMPLETION_REFRESH_MESSAGE_SECONDS = 1.0
13
+
11
14
 
12
15
  class CompletionRefresher:
13
16
  refreshers: dict = {}
14
17
 
15
- def __init__(self) -> None:
18
+ def __init__(self, invalidate_app: Callable[[], None] | None = None) -> None:
16
19
  self._completer_thread: threading.Thread | None = None
17
20
  self._restart_refresh = threading.Event()
21
+ self._refresh_visible_until = 0.0
22
+ self._visibility_timer: threading.Timer | None = None
23
+ self._invalidate_app = invalidate_app
18
24
 
19
25
  def refresh(
20
26
  self,
@@ -36,10 +42,14 @@ class CompletionRefresher:
36
42
  if completer_options is None:
37
43
  completer_options = {}
38
44
 
39
- if self.is_refreshing():
45
+ if self._thread_is_alive():
40
46
  self._restart_refresh.set()
41
47
  return [SQLResult(status="Auto-completion refresh restarted.")]
42
48
  else:
49
+ if self._visibility_timer is not None:
50
+ self._visibility_timer.cancel()
51
+ self._visibility_timer = None
52
+ self._refresh_visible_until = monotonic() + MIN_COMPLETION_REFRESH_MESSAGE_SECONDS
43
53
  self._completer_thread = threading.Thread(
44
54
  target=self._bg_refresh, args=(executor, callbacks, completer_options), name="completion_refresh"
45
55
  )
@@ -48,6 +58,9 @@ class CompletionRefresher:
48
58
  return [SQLResult(status="Auto-completion refresh started in the background.")]
49
59
 
50
60
  def is_refreshing(self) -> bool:
61
+ return self._thread_is_alive() or monotonic() < self._refresh_visible_until
62
+
63
+ def _thread_is_alive(self) -> bool:
51
64
  return bool(self._completer_thread and self._completer_thread.is_alive())
52
65
 
53
66
  def _bg_refresh(
@@ -73,31 +86,53 @@ class CompletionRefresher:
73
86
  e.ssl,
74
87
  )
75
88
  except pymysql.err.OperationalError:
89
+ self._finish_refreshing()
76
90
  return
77
91
 
78
- # If callbacks is a single function then push it into a list.
79
- if callable(callbacks):
80
- callbacks = [callbacks]
81
-
82
- while 1:
83
- for refresher in self.refreshers.values():
84
- refresher(completer, executor)
85
- if self._restart_refresh.is_set():
86
- self._restart_refresh.clear()
92
+ try:
93
+ # If callbacks is a single function then push it into a list.
94
+ if callable(callbacks):
95
+ callbacks = [callbacks]
96
+
97
+ while 1:
98
+ for refresher in self.refreshers.values():
99
+ refresher(completer, executor)
100
+ if self._restart_refresh.is_set():
101
+ self._restart_refresh.clear()
102
+ break
103
+ else:
104
+ # Break out of while loop if the for loop finishes natually
105
+ # without hitting the break statement.
87
106
  break
88
- else:
89
- # Break out of while loop if the for loop finishes natually
90
- # without hitting the break statement.
91
- break
92
107
 
93
- # Start over the refresh from the beginning if the for loop hit the
94
- # break statement.
95
- continue
108
+ # Start over the refresh from the beginning if the for loop hit the
109
+ # break statement.
110
+ continue
96
111
 
97
- for callback in callbacks:
98
- callback(completer)
112
+ for callback in callbacks:
113
+ callback(completer)
114
+ finally:
115
+ executor.close()
116
+ self._finish_refreshing()
99
117
 
100
- executor.close()
118
+ def _finish_refreshing(self) -> None:
119
+ self._invalidate()
120
+ remaining = self._refresh_visible_until - monotonic()
121
+ if remaining <= 0:
122
+ return
123
+ if self._visibility_timer is not None:
124
+ self._visibility_timer.cancel()
125
+ self._visibility_timer = threading.Timer(remaining, self._invalidate_after_visibility_deadline)
126
+ self._visibility_timer.daemon = True
127
+ self._visibility_timer.start()
128
+
129
+ def _invalidate_after_visibility_deadline(self) -> None:
130
+ self._visibility_timer = None
131
+ self._invalidate()
132
+
133
+ def _invalidate(self) -> None:
134
+ if self._invalidate_app is not None:
135
+ self._invalidate_app()
101
136
 
102
137
 
103
138
  def refresher(name: str, refreshers: dict = CompletionRefresher.refreshers) -> Callable:
@@ -24,15 +24,15 @@ def main_execute_from_cli(mycli: 'MyCli', cli_args: 'CliArgs') -> int:
24
24
  execute_sql = cli_args.execute
25
25
  if cli_args.format == 'csv':
26
26
  mycli.main_formatter.format_name = 'csv'
27
- if execute_sql.endswith(r'\G'):
27
+ if execute_sql.endswith((r'\G', r'\g', r'\x')):
28
28
  execute_sql = execute_sql[:-2]
29
29
  elif cli_args.format == 'tsv':
30
30
  mycli.main_formatter.format_name = 'tsv'
31
- if execute_sql.endswith(r'\G'):
31
+ if execute_sql.endswith((r'\G', r'\g', r'\x')):
32
32
  execute_sql = execute_sql[:-2]
33
33
  elif cli_args.format == 'table':
34
34
  mycli.main_formatter.format_name = 'ascii'
35
- if execute_sql.endswith(r'\G'):
35
+ if execute_sql.endswith((r'\G', r'\g', r'\x')):
36
36
  execute_sql = execute_sql[:-2]
37
37
  else:
38
38
  mycli.main_formatter.format_name = 'tsv'
@@ -9,6 +9,7 @@ from importlib import resources
9
9
  import os
10
10
  import random
11
11
  import re
12
+ import shutil
12
13
  import subprocess
13
14
  import sys
14
15
  import time
@@ -42,6 +43,7 @@ import mycli as mycli_package
42
43
  from mycli.clibuffer import cli_is_multiline
43
44
  from mycli.clistyle import style_factory_ptoolkit
44
45
  from mycli.clitoolbar import create_toolbar_tokens_func
46
+ from mycli.compat import WIN
45
47
  from mycli.constants import (
46
48
  DEFAULT_HOST,
47
49
  DEFAULT_WIDTH,
@@ -613,6 +615,7 @@ def _one_iteration(
613
615
 
614
616
  special.set_expanded_output(False)
615
617
  special.set_forced_horizontal_output(False)
618
+ special.set_explorer_output(False)
616
619
 
617
620
  try:
618
621
  text = handle_editor_command(
@@ -716,6 +719,7 @@ def _one_iteration(
716
719
  results = sqlexecute.run(text)
717
720
  mycli.main_formatter.query = text
718
721
  mycli.redirect_formatter.query = text
722
+ mycli.explorer_formatter.query = text
719
723
  successful = True
720
724
  _output_results(mycli, state, results, start)
721
725
  special.unset_once_if_written(mycli.post_redirect_command)
@@ -854,12 +858,25 @@ def _tips_picker() -> str:
854
858
  return random.choice(tips) if tips else r'\? or "help" for help!'
855
859
 
856
860
 
861
+ def _configure_editor(mycli: 'MyCli') -> None:
862
+ if configured_editor := mycli.config['editor'].get('editor_command'):
863
+ os.environ['VISUAL'] = configured_editor
864
+ elif not os.environ.get('VISUAL') and not os.environ.get('EDITOR'):
865
+ if shutil.which('code'):
866
+ os.environ['VISUAL'] = 'code --wait'
867
+ elif WIN:
868
+ os.environ['VISUAL'] = 'edit'
869
+ else:
870
+ os.environ['VISUAL'] = 'vi'
871
+
872
+
857
873
  def main_repl(mycli: 'MyCli') -> None:
858
874
  sqlexecute = mycli.sqlexecute
859
875
  assert sqlexecute is not None
860
876
  state = ReplState()
861
877
 
862
878
  mycli.configure_pager()
879
+ _configure_editor(mycli)
863
880
  if mycli.smart_completion and not mycli.sandbox_mode:
864
881
  mycli.refresh_completions()
865
882