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.
- vith-0.9.0/LICENSE +28 -0
- vith-0.9.0/PKG-INFO +180 -0
- vith-0.9.0/README.md +150 -0
- vith-0.9.0/pyproject.toml +71 -0
- vith-0.9.0/setup.cfg +4 -0
- vith-0.9.0/tests/test_cd.py +18 -0
- vith-0.9.0/tests/test_commands_grammar.py +14 -0
- vith-0.9.0/tests/test_completion.py +158 -0
- vith-0.9.0/tests/test_editor_buffer.py +265 -0
- vith-0.9.0/tests/test_io_backends.py +94 -0
- vith-0.9.0/tests/test_key_bindings_fixes.py +47 -0
- vith-0.9.0/tests/test_layout.py +257 -0
- vith-0.9.0/tests/test_lexer.py +132 -0
- vith-0.9.0/tests/test_preview.py +103 -0
- vith-0.9.0/tests/test_reporting.py +16 -0
- vith-0.9.0/tests/test_set_int_options.py +56 -0
- vith-0.9.0/tests/test_style.py +71 -0
- vith-0.9.0/tests/test_substitute.py +193 -0
- vith-0.9.0/tests/test_thai.py +894 -0
- vith-0.9.0/tests/test_thai_motions.py +208 -0
- vith-0.9.0/tests/test_window_arrangements.py +69 -0
- vith-0.9.0/tests/test_write_commands.py +91 -0
- vith-0.9.0/vith/__init__.py +25 -0
- vith-0.9.0/vith/__main__.py +7 -0
- vith-0.9.0/vith/commands/__init__.py +0 -0
- vith-0.9.0/vith/commands/commands.py +811 -0
- vith-0.9.0/vith/commands/completer.py +61 -0
- vith-0.9.0/vith/commands/grammar.py +50 -0
- vith-0.9.0/vith/commands/handler.py +60 -0
- vith-0.9.0/vith/commands/lexer.py +21 -0
- vith-0.9.0/vith/commands/preview.py +134 -0
- vith-0.9.0/vith/completion.py +224 -0
- vith-0.9.0/vith/editor.py +320 -0
- vith-0.9.0/vith/editor_buffer.py +265 -0
- vith-0.9.0/vith/entry_points/__init__.py +0 -0
- vith-0.9.0/vith/entry_points/run_vith.py +53 -0
- vith-0.9.0/vith/help.py +15 -0
- vith-0.9.0/vith/io/__init__.py +10 -0
- vith-0.9.0/vith/io/backends.py +169 -0
- vith-0.9.0/vith/io/base.py +48 -0
- vith-0.9.0/vith/key_bindings.py +323 -0
- vith-0.9.0/vith/layout.py +805 -0
- vith-0.9.0/vith/lexer.py +207 -0
- vith-0.9.0/vith/rc_file.py +56 -0
- vith-0.9.0/vith/reporting.py +134 -0
- vith-0.9.0/vith/style.py +168 -0
- vith-0.9.0/vith/thai.py +1461 -0
- vith-0.9.0/vith/welcome_message.py +48 -0
- vith-0.9.0/vith/window_arrangement.py +553 -0
- vith-0.9.0/vith.egg-info/PKG-INFO +180 -0
- vith-0.9.0/vith.egg-info/SOURCES.txt +53 -0
- vith-0.9.0/vith.egg-info/dependency_links.txt +1 -0
- vith-0.9.0/vith.egg-info/entry_points.txt +2 -0
- vith-0.9.0/vith.egg-info/requires.txt +7 -0
- 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,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
|