vith 0.9.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 (55) hide show
  1. vith-0.9.0/LICENSE +28 -0
  2. vith-0.9.0/PKG-INFO +180 -0
  3. vith-0.9.0/README.md +150 -0
  4. vith-0.9.0/pyproject.toml +71 -0
  5. vith-0.9.0/setup.cfg +4 -0
  6. vith-0.9.0/tests/test_cd.py +18 -0
  7. vith-0.9.0/tests/test_commands_grammar.py +14 -0
  8. vith-0.9.0/tests/test_completion.py +158 -0
  9. vith-0.9.0/tests/test_editor_buffer.py +265 -0
  10. vith-0.9.0/tests/test_io_backends.py +94 -0
  11. vith-0.9.0/tests/test_key_bindings_fixes.py +47 -0
  12. vith-0.9.0/tests/test_layout.py +257 -0
  13. vith-0.9.0/tests/test_lexer.py +132 -0
  14. vith-0.9.0/tests/test_preview.py +103 -0
  15. vith-0.9.0/tests/test_reporting.py +16 -0
  16. vith-0.9.0/tests/test_set_int_options.py +56 -0
  17. vith-0.9.0/tests/test_style.py +71 -0
  18. vith-0.9.0/tests/test_substitute.py +193 -0
  19. vith-0.9.0/tests/test_thai.py +894 -0
  20. vith-0.9.0/tests/test_thai_motions.py +208 -0
  21. vith-0.9.0/tests/test_window_arrangements.py +69 -0
  22. vith-0.9.0/tests/test_write_commands.py +91 -0
  23. vith-0.9.0/vith/__init__.py +25 -0
  24. vith-0.9.0/vith/__main__.py +7 -0
  25. vith-0.9.0/vith/commands/__init__.py +0 -0
  26. vith-0.9.0/vith/commands/commands.py +811 -0
  27. vith-0.9.0/vith/commands/completer.py +61 -0
  28. vith-0.9.0/vith/commands/grammar.py +50 -0
  29. vith-0.9.0/vith/commands/handler.py +60 -0
  30. vith-0.9.0/vith/commands/lexer.py +21 -0
  31. vith-0.9.0/vith/commands/preview.py +134 -0
  32. vith-0.9.0/vith/completion.py +224 -0
  33. vith-0.9.0/vith/editor.py +320 -0
  34. vith-0.9.0/vith/editor_buffer.py +265 -0
  35. vith-0.9.0/vith/entry_points/__init__.py +0 -0
  36. vith-0.9.0/vith/entry_points/run_vith.py +53 -0
  37. vith-0.9.0/vith/help.py +15 -0
  38. vith-0.9.0/vith/io/__init__.py +10 -0
  39. vith-0.9.0/vith/io/backends.py +169 -0
  40. vith-0.9.0/vith/io/base.py +48 -0
  41. vith-0.9.0/vith/key_bindings.py +323 -0
  42. vith-0.9.0/vith/layout.py +805 -0
  43. vith-0.9.0/vith/lexer.py +207 -0
  44. vith-0.9.0/vith/rc_file.py +56 -0
  45. vith-0.9.0/vith/reporting.py +134 -0
  46. vith-0.9.0/vith/style.py +168 -0
  47. vith-0.9.0/vith/thai.py +1461 -0
  48. vith-0.9.0/vith/welcome_message.py +48 -0
  49. vith-0.9.0/vith/window_arrangement.py +553 -0
  50. vith-0.9.0/vith.egg-info/PKG-INFO +180 -0
  51. vith-0.9.0/vith.egg-info/SOURCES.txt +53 -0
  52. vith-0.9.0/vith.egg-info/dependency_links.txt +1 -0
  53. vith-0.9.0/vith.egg-info/entry_points.txt +2 -0
  54. vith-0.9.0/vith.egg-info/requires.txt +7 -0
  55. vith-0.9.0/vith.egg-info/top_level.txt +1 -0
vith-0.9.0/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2015-2023, Jonathan Slenders
2
+ Copyright (c) 2026, Chaiwat Suttipongsakul
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification,
6
+ are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above copyright notice, this
12
+ list of conditions and the following disclaimer in the documentation and/or
13
+ other materials provided with the distribution.
14
+
15
+ * Neither the name of the {organization} nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
vith-0.9.0/PKG-INFO ADDED
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: vith
3
+ Version: 0.9.0
4
+ Summary: Vith — a pure-Python Vi clone with Thai & Lao script support (fork of pyvim)
5
+ Author: Jonathan Slenders
6
+ Author-email: Chaiwat Suttipongsakul <cwt@bashell.com>
7
+ Maintainer-email: Chaiwat Suttipongsakul <cwt@bashell.com>
8
+ License-Expression: BSD-3-Clause
9
+ Project-URL: Homepage, https://github.com/cwt/vith
10
+ Project-URL: Repository, https://github.com/cwt/vith.git
11
+ Project-URL: Upstream, https://github.com/prompt-toolkit/pyvim
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Topic :: Text Editors
19
+ Classifier: Topic :: Text Editors :: Text Processing
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: prompt_toolkit<3.1.0,>=3.0.0
24
+ Requires-Dist: pyflakes
25
+ Requires-Dist: pygments
26
+ Requires-Dist: docopt
27
+ Provides-Extra: jedi
28
+ Requires-Dist: jedi; extra == "jedi"
29
+ Dynamic: license-file
30
+
31
+ # vith (วิธ) — a fork of pyvim
32
+
33
+ **vith** (Thai: **วิธ**, from วิธี / Pali-Skt *vidhi*, "method"; also a portmanteau of **Vi**M + **TH**Ai) is a fork of [pyvim](https://github.com/prompt-toolkit/pyvim) — "an implementation of Vim in Python" originally created by Jonathan Slenders.
34
+
35
+ The upstream project was idle for several years, so this fork continues development independently. It adds **Thai & Lao script support** (terminal cell-cluster navigation and editing, two-tier word segmentation, SARA AM differential rendering) and **speed / latency / modern-Python stability** work (streamed + cached Pygments highlighting, lazy colorscheme loading, O(1) pyflakes error index, Python 3.14+/3.15 `asyncio` startup fix).
36
+
37
+ Full credit and history are preserved: Jonathan Slenders' original changelog is retained verbatim in the `CHANGELOG` appendix, and the BSD 3-clause license is retained with stacked copyright.
38
+
39
+ This fork is published as a separate PyPI package (`vith`) and is not a drop-in replacement for `pyvim`.
40
+
41
+ ---
42
+
43
+ *An implementation of Vim in Python*
44
+
45
+ ```sh
46
+ pip install vith
47
+ ```
48
+
49
+ This fork runs on **Python 3.10+** (`prompt_toolkit` 3.0) and adds two large, machine-verified plans (documented in the `docs/` Open Knowledge bundle):
50
+
51
+ - **Thai & Lao script support**: terminal cell-cluster navigation and editing (`h`, `l`, `j`, `k`, `x`, `X`, `s`, `r`, `~`, `$`, `e`...), two-tier Thai word segmentation (`libthai` with an always-available orthographic fallback) and SARA AM (`U+0E33`/`U+0EB3`) differential renderer fixes. See [`docs/thai_support_plan.md`](docs/thai_support_plan.md).
52
+ - **Speed, latency & modern-Python stability**: streamed + cached Pygments highlighting, per-line word-span caches for Thai word motions, lazy colorscheme loading, cached word completion, an O(1) pyflakes error index, and the Python 3.14+/3.15 `asyncio` startup-crash fix. See [`docs/speed_latency_plan.md`](docs/speed_latency_plan.md).
53
+
54
+ All changes are audited in [`docs/bugs/`](docs/bugs/) (findings, each reproduced and fixed); quality is enforced with pytest, mypy and ruff.
55
+
56
+ Issues, questions, wishes, comments, feedback, remarks? Please create a GitHub issue, I appreciate it.
57
+
58
+ ---
59
+
60
+ ## Installation
61
+
62
+ Simply install `vith` using pip:
63
+
64
+ ```sh
65
+ pip install vith
66
+ ```
67
+
68
+ For Python autocompletion support (via Jedi):
69
+
70
+ ```sh
71
+ pip install "vith[jedi]"
72
+ ```
73
+
74
+ It is a good idea to add the following to your `~/.bashrc` if you really want to use it:
75
+
76
+ ```sh
77
+ alias vi=vith
78
+ export EDITOR=vith
79
+ ```
80
+
81
+ ## The good things
82
+
83
+ The editor is written completely in Python. (There are no C extensions). This makes development a lot faster. It's easy to prototype and integrate new features.
84
+
85
+ We have already many nice things, for instance:
86
+
87
+ - Syntax highlighting of files, using the Pygments lexers.
88
+ - Horizontal and vertical splits, as well as tab pages. (Similar to Vim.)
89
+ - All of the functionality of [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit). This includes a lot of Vi key bindings, it's platform independent and runs every Python version covered by `python_requires >= 3.10`.
90
+ - Thai/Lao input files: cell-cluster safe navigation and *syntax-aware* word segmentation, including dictionary segmentation when `libthai` is installed.
91
+ - Several `:set ...` commands have been implemented, like `incsearch`, `number`, `ignorecase`, `wildmenu`, `expandtab`, `hlsearch`, `ruler`, `paste` and `tabstop`.
92
+ - Other working commands: `vsplit`, `tabnew`, `only`, `badd`, and many others.
93
+ - For Python source code, auto completion uses the amazing Jedi library, and code checking is done (asynchronously) through Pyflakes.
94
+ - Colorschemes can be changed at runtime.
95
+
96
+ Further, when the project develops, it should also become possible to write extensions in Python, and use Python as a scripting language. (Instead of vimscript, for instance.)
97
+
98
+ We can also do some cool stuff. Like for instance running the editor on the Python asyncio event loop and having other coroutines interact with the editor.
99
+
100
+ ## Configuring vith
101
+
102
+ It is possible to create a `.vithrc` file for a custom configuration (with graceful fallback to `~/.pyvimrc`). Have a look at this example: [`vithrc`](examples/config/vithrc)
103
+
104
+ ## Limitations
105
+
106
+ Compared to Vi Improved, Vith is still less powerful in many aspects.
107
+
108
+ - `prompt_toolkit` does not (or not yet) allow buffers to have an individual cursor when buffers are opened in several windows. Currently, this results in some unexpected behaviour, when a file is displayed in two windows at the same time. (The cursor could be displayed in the wrong window and other windows will sometimes scroll along when the cursor moves.) This has to be fixed in the future.
109
+ - The data structure for a buffer is extremely simple. (Right now, it's just a Python string, and an integer for the cursor position.) This works extremely well for development and quickly prototyping of new features.
110
+ - A lot of nice Vim features, like line folding, macros, etcetera are not yet implemented.
111
+ - Windows support is not that nice. It works, but could be improved.
112
+
113
+ ## Roadmap
114
+
115
+ Pull requests and issues are welcome. Feel free to create a GitHub issue to discuss ideas or report problems.
116
+
117
+ Maybe some day we will have a better data structure (Rope), that makes it possible to open really large files. (With good algorithms, Python does not have to be slower than C code.)
118
+
119
+ Maybe we will also have line folding and probably block editing. Maybe some day we will have a built-in Python debugger or mouse support. We'll see. :)
120
+
121
+ ## Testing
122
+
123
+ To run all tests, install the development requirements (pytest, mypy, ruff):
124
+
125
+ ```sh
126
+ python -m pip install -r requirements-dev.txt
127
+ ```
128
+
129
+ And then run from the root `vith` directory:
130
+
131
+ ```sh
132
+ python -m pytest tests
133
+ ```
134
+
135
+ Quality gates: `mypy .` (typing, checked against the 3.10 floor), `ruff check` (lint) and `ruff format --check .` (formatting). The reproducible measurements quoted in the docs come from:
136
+
137
+ ```sh
138
+ python tools/bench_plans.py
139
+ ```
140
+
141
+ ## Why was PyVim originally created?
142
+
143
+ *(From Jonathan Slenders' original PyVim documentation)*:
144
+
145
+ > "The main reason is maybe because it was a small step after I created the Python `prompt-toolkit` library. That is a library which is actually only a simply pure Python readline replacement, but with some nice additions like syntax highlighting and multiline editing. It was never intended to be a toolkit for full-screen terminal applications, but at some point I realised that everything we need for an editor was in there and I liked to challenge its design. So, I started an editor and the first proof of concept was literally just a few hundred lines of code, but it was already a working editor.
146
+ >
147
+ > The creation of `pyvim` will make sure that we have a solid architecture for `prompt-toolkit`, but it also aims to demonstrate the flexibility of the library. When it makes sense, features of `pyvim` will move back to `prompt-toolkit`, which in turn also results in a better Python REPL. (see [ptpython](https://github.com/prompt-toolkit/ptpython), an alternative REPL.)
148
+ >
149
+ > Above all, it is really fun to create an editor."
150
+
151
+ ## Why was the Vith fork created?
152
+
153
+ Upstream `pyvim` has been idle for several years. Vith was created to bring modern Python support and first-class Southeast Asian language capabilities to terminal-based editing:
154
+
155
+ - **Thai & Lao cell-cluster editing**: character-wise motions (`h`, `l`, `x`, `s`, `r`, `~`...) treat combining sequences as indivisible display cells, preventing dangling accents or cursor misalignment.
156
+ - **Syntax-aware word segmentation**: two-tier word boundaries combining dictionary lookup (`libthai`) with an orthographic cluster fallback.
157
+ - **Differential rendering fixes**: terminal diff-renderer updates for `SARA AM` (`U+0E33`/`U+0EB3`) and right-margin combining marks.
158
+ - **Performance & latency**: streamed Pygments syntax lexing, deferred colorscheme compilation, singleton word-completion caches, and an O(1) pyflakes error index.
159
+ - **Modern Python stability**: clean startup on Python 3.14+/3.15 asyncio event loops, PEP 621 packaging, and complete type annotations.
160
+
161
+ ## Alternatives
162
+
163
+ Certainly have a look at the alternatives:
164
+
165
+ - [Kaa](https://github.com/kaaedit/kaa) by @atsuoishimoto
166
+ - [Vai](https://github.com/stefanoborini/vai) by @stefanoborini
167
+ - [Vis](https://github.com/martanne/vis) by @martanne
168
+
169
+ ## Q & A
170
+
171
+ **Q: Do you use curses?**
172
+ **A:** No, it uses only `prompt-toolkit`.
173
+
174
+ ## Thanks
175
+
176
+ - To Jonathan Slenders, for creating PyVim and prompt_toolkit.
177
+ - To Vi Improved, by Bram Moolenaar, for the inspiration.
178
+ - To Jedi, pyflakes and docopt.
179
+ - To the Python wcwidth port of Jeff Quast for support of double width characters.
180
+ - To Guido van Rossum, for creating Python.
vith-0.9.0/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # vith (วิธ) — a fork of pyvim
2
+
3
+ **vith** (Thai: **วิธ**, from วิธี / Pali-Skt *vidhi*, "method"; also a portmanteau of **Vi**M + **TH**Ai) is a fork of [pyvim](https://github.com/prompt-toolkit/pyvim) — "an implementation of Vim in Python" originally created by Jonathan Slenders.
4
+
5
+ The upstream project was idle for several years, so this fork continues development independently. It adds **Thai & Lao script support** (terminal cell-cluster navigation and editing, two-tier word segmentation, SARA AM differential rendering) and **speed / latency / modern-Python stability** work (streamed + cached Pygments highlighting, lazy colorscheme loading, O(1) pyflakes error index, Python 3.14+/3.15 `asyncio` startup fix).
6
+
7
+ Full credit and history are preserved: Jonathan Slenders' original changelog is retained verbatim in the `CHANGELOG` appendix, and the BSD 3-clause license is retained with stacked copyright.
8
+
9
+ This fork is published as a separate PyPI package (`vith`) and is not a drop-in replacement for `pyvim`.
10
+
11
+ ---
12
+
13
+ *An implementation of Vim in Python*
14
+
15
+ ```sh
16
+ pip install vith
17
+ ```
18
+
19
+ This fork runs on **Python 3.10+** (`prompt_toolkit` 3.0) and adds two large, machine-verified plans (documented in the `docs/` Open Knowledge bundle):
20
+
21
+ - **Thai & Lao script support**: terminal cell-cluster navigation and editing (`h`, `l`, `j`, `k`, `x`, `X`, `s`, `r`, `~`, `$`, `e`...), two-tier Thai word segmentation (`libthai` with an always-available orthographic fallback) and SARA AM (`U+0E33`/`U+0EB3`) differential renderer fixes. See [`docs/thai_support_plan.md`](docs/thai_support_plan.md).
22
+ - **Speed, latency & modern-Python stability**: streamed + cached Pygments highlighting, per-line word-span caches for Thai word motions, lazy colorscheme loading, cached word completion, an O(1) pyflakes error index, and the Python 3.14+/3.15 `asyncio` startup-crash fix. See [`docs/speed_latency_plan.md`](docs/speed_latency_plan.md).
23
+
24
+ All changes are audited in [`docs/bugs/`](docs/bugs/) (findings, each reproduced and fixed); quality is enforced with pytest, mypy and ruff.
25
+
26
+ Issues, questions, wishes, comments, feedback, remarks? Please create a GitHub issue, I appreciate it.
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ Simply install `vith` using pip:
33
+
34
+ ```sh
35
+ pip install vith
36
+ ```
37
+
38
+ For Python autocompletion support (via Jedi):
39
+
40
+ ```sh
41
+ pip install "vith[jedi]"
42
+ ```
43
+
44
+ It is a good idea to add the following to your `~/.bashrc` if you really want to use it:
45
+
46
+ ```sh
47
+ alias vi=vith
48
+ export EDITOR=vith
49
+ ```
50
+
51
+ ## The good things
52
+
53
+ The editor is written completely in Python. (There are no C extensions). This makes development a lot faster. It's easy to prototype and integrate new features.
54
+
55
+ We have already many nice things, for instance:
56
+
57
+ - Syntax highlighting of files, using the Pygments lexers.
58
+ - Horizontal and vertical splits, as well as tab pages. (Similar to Vim.)
59
+ - All of the functionality of [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit). This includes a lot of Vi key bindings, it's platform independent and runs every Python version covered by `python_requires >= 3.10`.
60
+ - Thai/Lao input files: cell-cluster safe navigation and *syntax-aware* word segmentation, including dictionary segmentation when `libthai` is installed.
61
+ - Several `:set ...` commands have been implemented, like `incsearch`, `number`, `ignorecase`, `wildmenu`, `expandtab`, `hlsearch`, `ruler`, `paste` and `tabstop`.
62
+ - Other working commands: `vsplit`, `tabnew`, `only`, `badd`, and many others.
63
+ - For Python source code, auto completion uses the amazing Jedi library, and code checking is done (asynchronously) through Pyflakes.
64
+ - Colorschemes can be changed at runtime.
65
+
66
+ Further, when the project develops, it should also become possible to write extensions in Python, and use Python as a scripting language. (Instead of vimscript, for instance.)
67
+
68
+ We can also do some cool stuff. Like for instance running the editor on the Python asyncio event loop and having other coroutines interact with the editor.
69
+
70
+ ## Configuring vith
71
+
72
+ It is possible to create a `.vithrc` file for a custom configuration (with graceful fallback to `~/.pyvimrc`). Have a look at this example: [`vithrc`](examples/config/vithrc)
73
+
74
+ ## Limitations
75
+
76
+ Compared to Vi Improved, Vith is still less powerful in many aspects.
77
+
78
+ - `prompt_toolkit` does not (or not yet) allow buffers to have an individual cursor when buffers are opened in several windows. Currently, this results in some unexpected behaviour, when a file is displayed in two windows at the same time. (The cursor could be displayed in the wrong window and other windows will sometimes scroll along when the cursor moves.) This has to be fixed in the future.
79
+ - The data structure for a buffer is extremely simple. (Right now, it's just a Python string, and an integer for the cursor position.) This works extremely well for development and quickly prototyping of new features.
80
+ - A lot of nice Vim features, like line folding, macros, etcetera are not yet implemented.
81
+ - Windows support is not that nice. It works, but could be improved.
82
+
83
+ ## Roadmap
84
+
85
+ Pull requests and issues are welcome. Feel free to create a GitHub issue to discuss ideas or report problems.
86
+
87
+ Maybe some day we will have a better data structure (Rope), that makes it possible to open really large files. (With good algorithms, Python does not have to be slower than C code.)
88
+
89
+ Maybe we will also have line folding and probably block editing. Maybe some day we will have a built-in Python debugger or mouse support. We'll see. :)
90
+
91
+ ## Testing
92
+
93
+ To run all tests, install the development requirements (pytest, mypy, ruff):
94
+
95
+ ```sh
96
+ python -m pip install -r requirements-dev.txt
97
+ ```
98
+
99
+ And then run from the root `vith` directory:
100
+
101
+ ```sh
102
+ python -m pytest tests
103
+ ```
104
+
105
+ Quality gates: `mypy .` (typing, checked against the 3.10 floor), `ruff check` (lint) and `ruff format --check .` (formatting). The reproducible measurements quoted in the docs come from:
106
+
107
+ ```sh
108
+ python tools/bench_plans.py
109
+ ```
110
+
111
+ ## Why was PyVim originally created?
112
+
113
+ *(From Jonathan Slenders' original PyVim documentation)*:
114
+
115
+ > "The main reason is maybe because it was a small step after I created the Python `prompt-toolkit` library. That is a library which is actually only a simply pure Python readline replacement, but with some nice additions like syntax highlighting and multiline editing. It was never intended to be a toolkit for full-screen terminal applications, but at some point I realised that everything we need for an editor was in there and I liked to challenge its design. So, I started an editor and the first proof of concept was literally just a few hundred lines of code, but it was already a working editor.
116
+ >
117
+ > The creation of `pyvim` will make sure that we have a solid architecture for `prompt-toolkit`, but it also aims to demonstrate the flexibility of the library. When it makes sense, features of `pyvim` will move back to `prompt-toolkit`, which in turn also results in a better Python REPL. (see [ptpython](https://github.com/prompt-toolkit/ptpython), an alternative REPL.)
118
+ >
119
+ > Above all, it is really fun to create an editor."
120
+
121
+ ## Why was the Vith fork created?
122
+
123
+ Upstream `pyvim` has been idle for several years. Vith was created to bring modern Python support and first-class Southeast Asian language capabilities to terminal-based editing:
124
+
125
+ - **Thai & Lao cell-cluster editing**: character-wise motions (`h`, `l`, `x`, `s`, `r`, `~`...) treat combining sequences as indivisible display cells, preventing dangling accents or cursor misalignment.
126
+ - **Syntax-aware word segmentation**: two-tier word boundaries combining dictionary lookup (`libthai`) with an orthographic cluster fallback.
127
+ - **Differential rendering fixes**: terminal diff-renderer updates for `SARA AM` (`U+0E33`/`U+0EB3`) and right-margin combining marks.
128
+ - **Performance & latency**: streamed Pygments syntax lexing, deferred colorscheme compilation, singleton word-completion caches, and an O(1) pyflakes error index.
129
+ - **Modern Python stability**: clean startup on Python 3.14+/3.15 asyncio event loops, PEP 621 packaging, and complete type annotations.
130
+
131
+ ## Alternatives
132
+
133
+ Certainly have a look at the alternatives:
134
+
135
+ - [Kaa](https://github.com/kaaedit/kaa) by @atsuoishimoto
136
+ - [Vai](https://github.com/stefanoborini/vai) by @stefanoborini
137
+ - [Vis](https://github.com/martanne/vis) by @martanne
138
+
139
+ ## Q & A
140
+
141
+ **Q: Do you use curses?**
142
+ **A:** No, it uses only `prompt-toolkit`.
143
+
144
+ ## Thanks
145
+
146
+ - To Jonathan Slenders, for creating PyVim and prompt_toolkit.
147
+ - To Vi Improved, by Bram Moolenaar, for the inspiration.
148
+ - To Jedi, pyflakes and docopt.
149
+ - To the Python wcwidth port of Jeff Quast for support of double width characters.
150
+ - To Guido van Rossum, for creating Python.
@@ -0,0 +1,71 @@
1
+ # Tool configuration and PEP 621 packaging for vith.
2
+
3
+ [build-system]
4
+ requires = ["setuptools>=77"]
5
+ build-backend = "setuptools.build_meta"
6
+
7
+ [project]
8
+ name = "vith"
9
+ version = "0.9.0"
10
+ description = "Vith — a pure-Python Vi clone with Thai & Lao script support (fork of pyvim)"
11
+ readme = "README.md"
12
+ license = "BSD-3-Clause"
13
+ authors = [
14
+ { name = "Jonathan Slenders" },
15
+ { name = "Chaiwat Suttipongsakul", email = "cwt@bashell.com" },
16
+ ]
17
+ maintainers = [
18
+ { name = "Chaiwat Suttipongsakul", email = "cwt@bashell.com" },
19
+ ]
20
+ requires-python = ">=3.10"
21
+ dependencies = [
22
+ "prompt_toolkit>=3.0.0,<3.1.0",
23
+ "pyflakes",
24
+ "pygments",
25
+ "docopt",
26
+ ]
27
+ classifiers = [
28
+ "Development Status :: 4 - Beta",
29
+ "Environment :: Console",
30
+ "Intended Audience :: Developers",
31
+ "Operating System :: OS Independent",
32
+ "Programming Language :: Python :: 3",
33
+ "Programming Language :: Python :: 3.10",
34
+ "Topic :: Text Editors",
35
+ "Topic :: Text Editors :: Text Processing",
36
+ ]
37
+ [project.urls]
38
+ Homepage = "https://github.com/cwt/vith"
39
+ Repository = "https://github.com/cwt/vith.git"
40
+ Upstream = "https://github.com/prompt-toolkit/pyvim"
41
+
42
+ [project.optional-dependencies]
43
+ jedi = [
44
+ "jedi",
45
+ ]
46
+
47
+ [project.scripts]
48
+ vith = "vith.entry_points.run_vith:run"
49
+
50
+ [tool.setuptools.packages.find]
51
+ include = ["vith*"]
52
+
53
+ [tool.ruff]
54
+ target-version = "py310"
55
+ line-length = 100
56
+ extend-exclude = ["docs", "examples"]
57
+
58
+ [tool.ruff.lint]
59
+ select = ["E", "F", "W", "I"]
60
+ ignore = [
61
+ # `ruff format` keeps long string literals intact; do not fail lint on them.
62
+ "E501",
63
+ ]
64
+
65
+ [tool.ruff.lint.isort]
66
+ known-first-party = ["vith"]
67
+
68
+ [tool.ruff.format]
69
+ # Keep the existing mix of single/double quotes instead of rewriting every
70
+ # string in one sweep.
71
+ quote-style = "preserve"
vith-0.9.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ import os
2
+
3
+ from vith.commands.handler import handle_command
4
+
5
+
6
+ def test_cd_no_arg_and_expanduser(editor, tmp_path):
7
+ editor.load_initial_files([None])
8
+ home = os.path.expanduser('~')
9
+ # :cd with no argument goes home.
10
+ handle_command(editor, ':cd')
11
+ assert os.getcwd() == home
12
+ # :cd with ~/ path expands.
13
+ d = tmp_path / 'sub'
14
+ d.mkdir()
15
+ handle_command(editor, ':cd %s' % d)
16
+ assert os.getcwd() == str(d)
17
+ # restore
18
+ os.chdir(home)
@@ -0,0 +1,14 @@
1
+ from vith.commands.grammar import COMMAND_GRAMMAR
2
+ from vith.commands.handler import handle_command
3
+
4
+
5
+ def test_colo_matches_grammar():
6
+ m = COMMAND_GRAMMAR.match('colo default')
7
+ assert m is not None
8
+ assert m.variables().get('command') == 'colo'
9
+ assert m.variables().get('colorscheme') == 'default'
10
+
11
+
12
+ def test_unknown_multiword_command_shows_message(editor):
13
+ handle_command(editor, ':foo bar')
14
+ assert editor.message is not None
@@ -0,0 +1,158 @@
1
+ """
2
+ Tests for `vith/completion.py` (P3: singleton + line-cached word completer).
3
+ """
4
+
5
+ from prompt_toolkit.completion import CompleteEvent
6
+ from prompt_toolkit.document import Document
7
+
8
+ from vith.completion import DocumentCompleter, DocumentWordsCompleter
9
+
10
+
11
+ def _completions(completer, text, cursor):
12
+ doc = Document(text, cursor)
13
+ return [c.text for c in completer.get_completions(doc, CompleteEvent())]
14
+
15
+
16
+ def test_words_from_other_lines():
17
+ # 'f' on the cursor line matches 'foo' found in other lines:
18
+ comps = _completions(DocumentWordsCompleter(), 'foo bar baz\nf', 13)
19
+ assert 'foo' in comps
20
+
21
+
22
+ def test_words_from_current_line():
23
+ # 'foobar' on the non-cursor lines completes 'fo' typed on the
24
+ # current line:
25
+ comps = _completions(DocumentWordsCompleter(), 'foobar\nfo', 9)
26
+ assert comps == ['foobar']
27
+
28
+
29
+ def test_exact_word_before_cursor_not_offered():
30
+ # The word itself is never offered as its own completion:
31
+ comps = _completions(DocumentWordsCompleter(), 'foo bar\nfoo', 11)
32
+ assert comps == []
33
+
34
+
35
+ def test_no_word_before_cursor_yields_nothing():
36
+ comps = _completions(DocumentWordsCompleter(), 'foo bar\n ', 9)
37
+ assert comps == []
38
+
39
+
40
+ def test_cache_key_stable_while_typing_on_current_line():
41
+ comp = DocumentWordsCompleter()
42
+ assert comp._cached_key is None
43
+
44
+ c1 = _completions(comp, 'alpha beta\nal', 13)
45
+ assert c1 == ['alpha']
46
+ key = comp._cached_key
47
+ assert key is not None
48
+
49
+ # Typing on the current line leaves the prefix/suffix around that line
50
+ # unchanged: the non-cursor word cache must *not* be recomputed.
51
+ c2 = _completions(comp, 'alpha beta\nalp', 14)
52
+ assert c2 == ['alpha']
53
+ assert comp._cached_key == key, 'typing on the current line must not rescan the other lines'
54
+
55
+
56
+ def test_cache_invalidated_when_other_lines_change():
57
+ comp = DocumentWordsCompleter()
58
+ _completions(comp, 'alpha beta\nal', 13)
59
+ key_before = comp._cached_key
60
+
61
+ # 'alpine' appears on a non-cursor line of the *new* text:
62
+ c3 = _completions(comp, 'alpha zebra alpine\nal', 21)
63
+ assert c3 == ['alpha', 'alpine']
64
+ assert comp._cached_key != key_before, (
65
+ 'changed other lines must invalidate the non-cursor word cache'
66
+ )
67
+
68
+
69
+ def test_document_completer_persists_words_completer(editor, editor_buffer):
70
+ # P3: `DocumentCompleter` must reuse a single `DocumentWordsCompleter`
71
+ # across invocations (the cache is only useful if it persists).
72
+ editor.enable_jedi = False
73
+ editor_buffer.location = None # treated as '.txt' -> words completer
74
+
75
+ completer = DocumentCompleter(editor, editor_buffer)
76
+ assert isinstance(completer._words_completer, DocumentWordsCompleter)
77
+ assert completer._words_completer._cached_key is None
78
+
79
+ doc = Document('gamma alpha\ngamm', 16)
80
+ assert 'gamma' in [c.text for c in completer.get_completions(doc, CompleteEvent())]
81
+ assert completer._words_completer._cached_key is not None
82
+
83
+ words_completer = completer._words_completer
84
+ list(completer.get_completions(doc, CompleteEvent()))
85
+ assert completer._words_completer is words_completer, (
86
+ 'the words completer instance must persist across calls'
87
+ )
88
+
89
+
90
+ def test_document_completer_python_uses_jedi(editor, editor_buffer, tmp_path):
91
+ import pytest
92
+
93
+ pytest.importorskip('jedi')
94
+
95
+ p = tmp_path / 'mod.py'
96
+ p.write_text('import os\n')
97
+ editor.enable_jedi = True
98
+ editor_buffer.location = str(p)
99
+
100
+ completer = DocumentCompleter(editor, editor_buffer)
101
+ doc = Document('import o', 8)
102
+ # Iterate: this must exercise the Jedi path (both old `completions()`
103
+ # and new `complete(line, column)` APIs) and not the words completer.
104
+ names = [c.text for c in completer.get_completions(doc, CompleteEvent())]
105
+ assert 'os' in names
106
+ assert completer._words_completer._cached_key is None, (
107
+ '.py files with Jedi enabled must not use the words completer'
108
+ )
109
+ # The jedi API introspection is computed once per session (docs/bugs/030.md):
110
+ import vith.completion as _pc
111
+
112
+ assert _pc._JEDI_TAKES_COLUMN is False
113
+
114
+
115
+ def test_document_completer_falls_back_without_jedi(editor, editor_buffer, monkeypatch):
116
+ # jedi is optional: when the module is missing (or the probe is forced
117
+ # off), `.py` buffers must complete with the words completer instead of
118
+ # raising ImportError on the first Tab press (docs/bugs/003.md).
119
+ monkeypatch.setattr('vith.completion._jedi_available', lambda: False)
120
+ editor.enable_jedi = True
121
+ editor_buffer.location = '/some/project/module.py'
122
+
123
+ completer = DocumentCompleter(editor, editor_buffer)
124
+ doc = Document('gamma alpha\ngamm', 16)
125
+ names = [c.text for c in completer.get_completions(doc, CompleteEvent())]
126
+ assert names == ['gamma']
127
+
128
+ # And with no word before the cursor the fallback stays silent.
129
+ doc = Document('import o', 8)
130
+ assert [c.text for c in completer.get_completions(doc, CompleteEvent())] == []
131
+
132
+
133
+ def test_python_completer_missing_module_degrades_to_no_completions(
134
+ editor, editor_buffer, monkeypatch
135
+ ):
136
+ # Even if selection ever picks `_PythonCompleter`, a missing jedi module
137
+ # must degrade to "no completions" rather than crash (ImportError guard).
138
+ import sys
139
+
140
+ import vith.completion as completion_module
141
+
142
+ monkeypatch.setitem(sys.modules, 'jedi', None) # `import jedi` -> ImportError
143
+ monkeypatch.setattr(completion_module, '_jedi_available', lambda: True)
144
+
145
+ editor.enable_jedi = True
146
+ editor_buffer.location = '/some/project/module.py'
147
+ completer = DocumentCompleter(editor, editor_buffer)
148
+
149
+ doc = Document('import o', 8)
150
+ list(completer.get_completions(doc, CompleteEvent())) # must not raise
151
+
152
+
153
+ def test_thai_words_not_split_at_combining_marks():
154
+ text = 'ที่ดิน น้ำ ผู้ใหญ่\nที่'
155
+ comps = _completions(DocumentWordsCompleter(), text, len('ที่ดิน น้ำ ผู้ใหญ่\nที่'))
156
+ assert 'ที่ดิน' in comps or 'ดิน' not in comps
157
+ # The corrupted single-base token 'ใหญ' (missing tone mark) must not appear.
158
+ assert 'ใหญ' not in comps