dtab 0.2.0__tar.gz → 0.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.
- {dtab-0.2.0 → dtab-0.3.0}/PKG-INFO +13 -7
- {dtab-0.2.0 → dtab-0.3.0}/README.md +12 -6
- {dtab-0.2.0 → dtab-0.3.0}/dtab.egg-info/PKG-INFO +13 -7
- {dtab-0.2.0 → dtab-0.3.0}/dtab.py +16 -11
- {dtab-0.2.0 → dtab-0.3.0}/test/test_dtab.py +75 -16
- {dtab-0.2.0 → dtab-0.3.0}/dtab.egg-info/SOURCES.txt +0 -0
- {dtab-0.2.0 → dtab-0.3.0}/dtab.egg-info/dependency_links.txt +0 -0
- {dtab-0.2.0 → dtab-0.3.0}/dtab.egg-info/entry_points.txt +0 -0
- {dtab-0.2.0 → dtab-0.3.0}/dtab.egg-info/requires.txt +0 -0
- {dtab-0.2.0 → dtab-0.3.0}/dtab.egg-info/top_level.txt +0 -0
- {dtab-0.2.0 → dtab-0.3.0}/pyproject.toml +0 -0
- {dtab-0.2.0 → dtab-0.3.0}/setup.cfg +0 -0
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dtab
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Delta Tab: config files made of tab-separated paths. One line is one path into a tree.
|
|
5
5
|
Project-URL: Homepage, https://github.com/RyannDaGreat/dtab
|
|
6
6
|
Requires-Python: >=3.8
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: fire
|
|
9
9
|
|
|
10
|
-
<p align="center"><img src="https://raw.githubusercontent.com/RyannDaGreat/dtab/main/assets/logo.jpg" alt="dtab" width="640"></p>
|
|
10
|
+
<!-- <p align="center"><img src="https://raw.githubusercontent.com/RyannDaGreat/dtab/main/assets/logo.jpg" alt="dtab" width="640"></p> -->
|
|
11
|
+
<!-- <img width="6912" height="4188" alt="image" src="https://github.com/user-attachments/assets/db10af67-b7ba-4e42-a808-946c8cdcc2c7" /> -->
|
|
12
|
+
<img width="6912" height="2940" alt="image" src="https://github.com/user-attachments/assets/24f2187b-9e71-45e6-9a4b-427ad00e48c8" />
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
<p align="center"><sub>Logo made with <a href="https://github.com/RyannDaGreat/SvelteLib">PowerRP</a>. <a href="https://ryanndagreat.github.io/SvelteLib/?repo=RyannDaGreat/dtab">Open it in PowerRP</a> to edit it (source: <code>assets/doc.json</code>).</sub></p>
|
|
12
17
|
|
|
13
18
|
# dtab
|
|
@@ -32,7 +37,7 @@ dtab.parse(open("scene.dtab").read())
|
|
|
32
37
|
|
|
33
38
|
- Less to look at. No braces, quotes, or commas between values. A file with its tabs aligned reads like pseudocode, and is easy to write by hand, even on paper.
|
|
34
39
|
- Simple. Six rules, one pass, about 70 lines per implementation.
|
|
35
|
-
- Everything is addressable. There are no lists, so every value has a
|
|
40
|
+
- Everything is addressable. There are no lists, so every value has a path: `config.deltas.l1.position.x` with EasyDict in Python or plain property access in JavaScript, brackets for keys like `file.json`.
|
|
36
41
|
- You choose the shape. Lines stack, and `c,d` writes one value under several keys, so the same tree can be written wide, deep, or on one line, trading horizontal space for vertical. These are the same file:
|
|
37
42
|
|
|
38
43
|
```
|
|
@@ -59,8 +64,9 @@ dtab.parse(open("scene.dtab").read())
|
|
|
59
64
|
- An entry that starts with a space is a comment.
|
|
60
65
|
- `$key` starts a multiline string. See below.
|
|
61
66
|
|
|
62
|
-
Every value is a string. Cast the ones you need.
|
|
63
|
-
so
|
|
67
|
+
Every value is a string. Cast the ones you need. A key is any run of letters, digits, `_`, `.` and `-`,
|
|
68
|
+
so `file.json`, `2026-09-07` and `0` are keys. Keys that happen to be identifiers work as attributes
|
|
69
|
+
(`config.deltas.l1` with EasyDict and friends); the rest are reached with brackets (`config["file.json"]`).
|
|
64
70
|
|
|
65
71
|
## Multiline strings
|
|
66
72
|
|
|
@@ -92,8 +98,8 @@ the value. Unknown tags are plain text, never an error.
|
|
|
92
98
|
|
|
93
99
|
- **Python**: `pip install dtab` then `import dtab`
|
|
94
100
|
- **JavaScript**: `npm install deltatab` then `const dtab = require('deltatab')`, or `<script src="https://cdn.jsdelivr.net/npm/deltatab/dtab.js">` for `window.dtab`
|
|
95
|
-
- **Vim**: `Plugin 'RyannDaGreat/dtab'` (Vundle) or `Plug 'RyannDaGreat/dtab'` (vim-plug), or paste `dtab.vim` into your vimrc. Highlights `*.dtab`, flags bad keys and trailing tabs,
|
|
96
|
-
- **VS Code**: search "dtab" in Extensions, or `code --install-extension RyannDaGreat.dtab`. Highlighting
|
|
101
|
+
- **Vim**: `Plugin 'RyannDaGreat/dtab'` (Vundle) or `Plug 'RyannDaGreat/dtab'` (vim-plug), or paste `dtab.vim` into your vimrc. Highlights `*.dtab`, flags bad keys and trailing tabs, gives the file a Δ icon in NERDTree if vim-devicons is installed, and `:DtabPreview` opens a split showing the tree as JSON that follows your edits (needs `+python3`).
|
|
102
|
+
- **VS Code**: search "dtab" in Extensions, or `code --install-extension RyannDaGreat.dtab`. Highlighting, a Δ file icon, and a live JSON preview of the file beside it (the preview button in the title bar, or Cmd+K V).
|
|
97
103
|
|
|
98
104
|
## API
|
|
99
105
|
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
<p align="center"><img src="https://raw.githubusercontent.com/RyannDaGreat/dtab/main/assets/logo.jpg" alt="dtab" width="640"></p>
|
|
1
|
+
<!-- <p align="center"><img src="https://raw.githubusercontent.com/RyannDaGreat/dtab/main/assets/logo.jpg" alt="dtab" width="640"></p> -->
|
|
2
|
+
<!-- <img width="6912" height="4188" alt="image" src="https://github.com/user-attachments/assets/db10af67-b7ba-4e42-a808-946c8cdcc2c7" /> -->
|
|
3
|
+
<img width="6912" height="2940" alt="image" src="https://github.com/user-attachments/assets/24f2187b-9e71-45e6-9a4b-427ad00e48c8" />
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
2
7
|
<p align="center"><sub>Logo made with <a href="https://github.com/RyannDaGreat/SvelteLib">PowerRP</a>. <a href="https://ryanndagreat.github.io/SvelteLib/?repo=RyannDaGreat/dtab">Open it in PowerRP</a> to edit it (source: <code>assets/doc.json</code>).</sub></p>
|
|
3
8
|
|
|
4
9
|
# dtab
|
|
@@ -23,7 +28,7 @@ dtab.parse(open("scene.dtab").read())
|
|
|
23
28
|
|
|
24
29
|
- Less to look at. No braces, quotes, or commas between values. A file with its tabs aligned reads like pseudocode, and is easy to write by hand, even on paper.
|
|
25
30
|
- Simple. Six rules, one pass, about 70 lines per implementation.
|
|
26
|
-
- Everything is addressable. There are no lists, so every value has a
|
|
31
|
+
- Everything is addressable. There are no lists, so every value has a path: `config.deltas.l1.position.x` with EasyDict in Python or plain property access in JavaScript, brackets for keys like `file.json`.
|
|
27
32
|
- You choose the shape. Lines stack, and `c,d` writes one value under several keys, so the same tree can be written wide, deep, or on one line, trading horizontal space for vertical. These are the same file:
|
|
28
33
|
|
|
29
34
|
```
|
|
@@ -50,8 +55,9 @@ dtab.parse(open("scene.dtab").read())
|
|
|
50
55
|
- An entry that starts with a space is a comment.
|
|
51
56
|
- `$key` starts a multiline string. See below.
|
|
52
57
|
|
|
53
|
-
Every value is a string. Cast the ones you need.
|
|
54
|
-
so
|
|
58
|
+
Every value is a string. Cast the ones you need. A key is any run of letters, digits, `_`, `.` and `-`,
|
|
59
|
+
so `file.json`, `2026-09-07` and `0` are keys. Keys that happen to be identifiers work as attributes
|
|
60
|
+
(`config.deltas.l1` with EasyDict and friends); the rest are reached with brackets (`config["file.json"]`).
|
|
55
61
|
|
|
56
62
|
## Multiline strings
|
|
57
63
|
|
|
@@ -83,8 +89,8 @@ the value. Unknown tags are plain text, never an error.
|
|
|
83
89
|
|
|
84
90
|
- **Python**: `pip install dtab` then `import dtab`
|
|
85
91
|
- **JavaScript**: `npm install deltatab` then `const dtab = require('deltatab')`, or `<script src="https://cdn.jsdelivr.net/npm/deltatab/dtab.js">` for `window.dtab`
|
|
86
|
-
- **Vim**: `Plugin 'RyannDaGreat/dtab'` (Vundle) or `Plug 'RyannDaGreat/dtab'` (vim-plug), or paste `dtab.vim` into your vimrc. Highlights `*.dtab`, flags bad keys and trailing tabs,
|
|
87
|
-
- **VS Code**: search "dtab" in Extensions, or `code --install-extension RyannDaGreat.dtab`. Highlighting
|
|
92
|
+
- **Vim**: `Plugin 'RyannDaGreat/dtab'` (Vundle) or `Plug 'RyannDaGreat/dtab'` (vim-plug), or paste `dtab.vim` into your vimrc. Highlights `*.dtab`, flags bad keys and trailing tabs, gives the file a Δ icon in NERDTree if vim-devicons is installed, and `:DtabPreview` opens a split showing the tree as JSON that follows your edits (needs `+python3`).
|
|
93
|
+
- **VS Code**: search "dtab" in Extensions, or `code --install-extension RyannDaGreat.dtab`. Highlighting, a Δ file icon, and a live JSON preview of the file beside it (the preview button in the title bar, or Cmd+K V).
|
|
88
94
|
|
|
89
95
|
## API
|
|
90
96
|
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dtab
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Delta Tab: config files made of tab-separated paths. One line is one path into a tree.
|
|
5
5
|
Project-URL: Homepage, https://github.com/RyannDaGreat/dtab
|
|
6
6
|
Requires-Python: >=3.8
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: fire
|
|
9
9
|
|
|
10
|
-
<p align="center"><img src="https://raw.githubusercontent.com/RyannDaGreat/dtab/main/assets/logo.jpg" alt="dtab" width="640"></p>
|
|
10
|
+
<!-- <p align="center"><img src="https://raw.githubusercontent.com/RyannDaGreat/dtab/main/assets/logo.jpg" alt="dtab" width="640"></p> -->
|
|
11
|
+
<!-- <img width="6912" height="4188" alt="image" src="https://github.com/user-attachments/assets/db10af67-b7ba-4e42-a808-946c8cdcc2c7" /> -->
|
|
12
|
+
<img width="6912" height="2940" alt="image" src="https://github.com/user-attachments/assets/24f2187b-9e71-45e6-9a4b-427ad00e48c8" />
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
<p align="center"><sub>Logo made with <a href="https://github.com/RyannDaGreat/SvelteLib">PowerRP</a>. <a href="https://ryanndagreat.github.io/SvelteLib/?repo=RyannDaGreat/dtab">Open it in PowerRP</a> to edit it (source: <code>assets/doc.json</code>).</sub></p>
|
|
12
17
|
|
|
13
18
|
# dtab
|
|
@@ -32,7 +37,7 @@ dtab.parse(open("scene.dtab").read())
|
|
|
32
37
|
|
|
33
38
|
- Less to look at. No braces, quotes, or commas between values. A file with its tabs aligned reads like pseudocode, and is easy to write by hand, even on paper.
|
|
34
39
|
- Simple. Six rules, one pass, about 70 lines per implementation.
|
|
35
|
-
- Everything is addressable. There are no lists, so every value has a
|
|
40
|
+
- Everything is addressable. There are no lists, so every value has a path: `config.deltas.l1.position.x` with EasyDict in Python or plain property access in JavaScript, brackets for keys like `file.json`.
|
|
36
41
|
- You choose the shape. Lines stack, and `c,d` writes one value under several keys, so the same tree can be written wide, deep, or on one line, trading horizontal space for vertical. These are the same file:
|
|
37
42
|
|
|
38
43
|
```
|
|
@@ -59,8 +64,9 @@ dtab.parse(open("scene.dtab").read())
|
|
|
59
64
|
- An entry that starts with a space is a comment.
|
|
60
65
|
- `$key` starts a multiline string. See below.
|
|
61
66
|
|
|
62
|
-
Every value is a string. Cast the ones you need.
|
|
63
|
-
so
|
|
67
|
+
Every value is a string. Cast the ones you need. A key is any run of letters, digits, `_`, `.` and `-`,
|
|
68
|
+
so `file.json`, `2026-09-07` and `0` are keys. Keys that happen to be identifiers work as attributes
|
|
69
|
+
(`config.deltas.l1` with EasyDict and friends); the rest are reached with brackets (`config["file.json"]`).
|
|
64
70
|
|
|
65
71
|
## Multiline strings
|
|
66
72
|
|
|
@@ -92,8 +98,8 @@ the value. Unknown tags are plain text, never an error.
|
|
|
92
98
|
|
|
93
99
|
- **Python**: `pip install dtab` then `import dtab`
|
|
94
100
|
- **JavaScript**: `npm install deltatab` then `const dtab = require('deltatab')`, or `<script src="https://cdn.jsdelivr.net/npm/deltatab/dtab.js">` for `window.dtab`
|
|
95
|
-
- **Vim**: `Plugin 'RyannDaGreat/dtab'` (Vundle) or `Plug 'RyannDaGreat/dtab'` (vim-plug), or paste `dtab.vim` into your vimrc. Highlights `*.dtab`, flags bad keys and trailing tabs,
|
|
96
|
-
- **VS Code**: search "dtab" in Extensions, or `code --install-extension RyannDaGreat.dtab`. Highlighting
|
|
101
|
+
- **Vim**: `Plugin 'RyannDaGreat/dtab'` (Vundle) or `Plug 'RyannDaGreat/dtab'` (vim-plug), or paste `dtab.vim` into your vimrc. Highlights `*.dtab`, flags bad keys and trailing tabs, gives the file a Δ icon in NERDTree if vim-devicons is installed, and `:DtabPreview` opens a split showing the tree as JSON that follows your edits (needs `+python3`).
|
|
102
|
+
- **VS Code**: search "dtab" in Extensions, or `code --install-extension RyannDaGreat.dtab`. Highlighting, a Δ file icon, and a live JSON preview of the file beside it (the preview button in the title bar, or Cmd+K V).
|
|
97
103
|
|
|
98
104
|
## API
|
|
99
105
|
|
|
@@ -21,7 +21,8 @@ Rules:
|
|
|
21
21
|
- `$key` is a multiline leaf: its value is the entries after it on its line plus every line indented
|
|
22
22
|
under it, one line each, with their common indentation removed. A word after the key on the `$` line
|
|
23
23
|
(`$query sql`) is a language tag for editors and is not part of the value.
|
|
24
|
-
- Keys are
|
|
24
|
+
- Keys are one or more letters, digits, or KEY_PUNCTUATION (`_.-`), so `file.json` and `123aa` are keys. Keys that
|
|
25
|
+
are also Python identifiers work as attributes (config.deltas.l1). Every value is a string.
|
|
25
26
|
|
|
26
27
|
Single pass, one stack, O(total characters).
|
|
27
28
|
"""
|
|
@@ -29,12 +30,14 @@ Single pass, one stack, O(total characters).
|
|
|
29
30
|
import json
|
|
30
31
|
import re
|
|
31
32
|
|
|
32
|
-
__version__ = "0.
|
|
33
|
+
__version__ = "0.3.0" # SEMANTIC BINDING: dtab-version (also package.json "version")
|
|
33
34
|
|
|
34
35
|
KEY_SEPARATOR = "," # a,b writes the same value under each key
|
|
35
36
|
BLOCK_PREFIX = "$" # $key: a leaf whose value is the lines under it ($ as in string)
|
|
36
|
-
|
|
37
|
+
KEY_PUNCTUATION = "_.-" # Allowed in keys besides letters and digits. SEMANTIC BINDING: dtab-key-punctuation
|
|
38
|
+
KEY_RULE = "keys may contain only letters, digits and " + " ".join(KEY_PUNCTUATION)
|
|
37
39
|
_TAB_RUN = re.compile(r"\t+") # Several tabs in a row are one separator, so columns can be aligned
|
|
40
|
+
_KEY = re.compile(r"[\w" + re.escape(KEY_PUNCTUATION) + "]+") # \w: letters, digits, _ (Unicode); the rest is reserved for syntax
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
def parse(text):
|
|
@@ -57,9 +60,11 @@ def parse(text):
|
|
|
57
60
|
{'query': 'SELECT *\\n\\n\\tFROM users', 'next': '1'}
|
|
58
61
|
>>> parse('$cmd\\tpip install rp\\tpython train.py')
|
|
59
62
|
{'cmd': 'pip install rp\\npython train.py'}
|
|
60
|
-
>>> parse('a\\tb 1\\
|
|
63
|
+
>>> parse('a\\tb 1\\nfile.json\\tsize 2\\n123aa 3')
|
|
64
|
+
{'a': {'b': '1'}, 'file.json': {'size': '2'}, '123aa': '3'}
|
|
65
|
+
>>> parse('a\\tb 1\\nc/d\\te 2')
|
|
61
66
|
Traceback (most recent call last):
|
|
62
|
-
ValueError: dtab line 2: invalid key 'c
|
|
67
|
+
ValueError: dtab line 2: invalid key 'c/d': keys may contain only letters, digits and _ . -
|
|
63
68
|
"""
|
|
64
69
|
root = {}
|
|
65
70
|
stack = [(-1, [root])] # (indent, nodes that deeper lines nest into)
|
|
@@ -126,19 +131,19 @@ def stringify(tree):
|
|
|
126
131
|
|
|
127
132
|
def _key_names(key, line_number, allow_commas):
|
|
128
133
|
"""
|
|
129
|
-
Pure function (raises ValueError). The
|
|
130
|
-
|
|
134
|
+
Pure function (raises ValueError). The names a key stands for: `a,b` is two while parsing, and a
|
|
135
|
+
stringify key must be a single name.
|
|
131
136
|
|
|
132
137
|
Examples:
|
|
133
|
-
>>> _key_names('l1,l2', 1, True), _key_names('
|
|
134
|
-
(['l1', 'l2'], ['
|
|
138
|
+
>>> _key_names('l1,l2', 1, True), _key_names('file-thing.json', None, False)
|
|
139
|
+
(['l1', 'l2'], ['file-thing.json'])
|
|
135
140
|
>>> _key_names('a,b', None, False)
|
|
136
141
|
Traceback (most recent call last):
|
|
137
|
-
ValueError: dtab: invalid key 'a,b': keys
|
|
142
|
+
ValueError: dtab: invalid key 'a,b': keys may contain only letters, digits and _ . -
|
|
138
143
|
"""
|
|
139
144
|
names = key.split(KEY_SEPARATOR) if allow_commas else [key]
|
|
140
145
|
for name in names:
|
|
141
|
-
if not
|
|
146
|
+
if not _KEY.fullmatch(name):
|
|
142
147
|
where = " line %d" % line_number if line_number else ""
|
|
143
148
|
raise ValueError("dtab%s: invalid key %r: %s" % (where, key, KEY_RULE))
|
|
144
149
|
return names
|
|
@@ -14,19 +14,23 @@ Checks:
|
|
|
14
14
|
that is one leaf, deltas.initial.l1.intensity, where the original ignored the later `l1 intensity 1`),
|
|
15
15
|
and `$` multiline blocks, which the original did not have.
|
|
16
16
|
4. parse(stringify(parse(text))) == parse(text) for every sample.
|
|
17
|
-
5. The key rule: bad keys are rejected with a line number in parse and in
|
|
18
|
-
|
|
17
|
+
5. The key rule (letters, digits, _ . -): bad keys are rejected with a line number in parse and in
|
|
18
|
+
stringify, and the Python and JS character classes agree on a set of Unicode probes.
|
|
19
19
|
6. node test/test_dtab.js.
|
|
20
20
|
7. Vim: the syntax groups over test/samples/highlight.dtab match test/expected/highlight.txt byte by byte
|
|
21
|
-
(that sample deliberately contains invalid keys, so it is not parsed),
|
|
22
|
-
filetype when the repo is on 'runtimepath',
|
|
21
|
+
(that sample deliberately contains invalid keys, so it is not parsed), embedded languages, the Tab and
|
|
22
|
+
shift keys, :DtabPreview, and plugin/dtab.vim sets the filetype when the repo is on 'runtimepath',
|
|
23
|
+
which is what Vundle and vim-plug do.
|
|
23
24
|
8. docs/index.html in headless Chrome (test/test_web.js), skipped with a message if puppeteer is not
|
|
24
25
|
installed (`npm install --no-save puppeteer && npx puppeteer browsers install chrome`).
|
|
25
|
-
9. The VS Code extension's TextMate grammar tokenizes the vim highlight sample the same way vim does
|
|
26
|
-
(test/test_vscode.js), skipped if vscode-textmate is not installed
|
|
26
|
+
9. The VS Code extension's TextMate grammar tokenizes the vim highlight sample the same way vim does,
|
|
27
|
+
and its indentation and preview logic (test/test_vscode.js), skipped if vscode-textmate is not installed
|
|
27
28
|
(`npm install --no-save vscode-textmate vscode-oniguruma`).
|
|
29
|
+
10. The JSON preview inside the installed VS Code, in an isolated profile (test/test_vscode_live.js),
|
|
30
|
+
skipped if @vscode/test-electron is not installed.
|
|
28
31
|
|
|
29
|
-
Needs: python 3, node, vim.
|
|
32
|
+
Needs: python 3, node, vim. The optional packages go in together, since an `npm install --no-save` removes
|
|
33
|
+
the ones it was not given: `npm install --no-save puppeteer vscode-textmate vscode-oniguruma @vscode/test-electron`.
|
|
30
34
|
"""
|
|
31
35
|
|
|
32
36
|
import doctest
|
|
@@ -40,7 +44,7 @@ ROOT = Path(__file__).resolve().parent.parent
|
|
|
40
44
|
HIGHLIGHT_SAMPLE = ROOT / "test" / "samples" / "highlight.dtab"
|
|
41
45
|
SAMPLES = sorted(path for path in (ROOT / "test" / "samples").glob("*.dtab") if path != HIGHLIGHT_SAMPLE)
|
|
42
46
|
DEVIATING = {"deviations.dtab", "game_config.dtab", "multiline.dtab"} # multiline: $ blocks did not exist in the original
|
|
43
|
-
IDENTIFIER_PROBES = ["café", "变量", "x²", "_x", "0x", "
|
|
47
|
+
IDENTIFIER_PROBES = ["café", "变量", "x²", "_x", "0x", "a-b", "ok_1", "ª", "Ⅻ", "℘", "ℕ", "𝔸", "a.b", "é1", "1é", "a/b", "a:b", "ä-ö.ü", "١٢٣", "a~b"] # the key rule, py vs js
|
|
44
48
|
|
|
45
49
|
sys.path.insert(0, str(ROOT))
|
|
46
50
|
import dtab # noqa: E402
|
|
@@ -88,20 +92,28 @@ def test_round_trips():
|
|
|
88
92
|
|
|
89
93
|
def test_key_rule():
|
|
90
94
|
for text, fragments in [
|
|
91
|
-
("a\
|
|
92
|
-
("ok 1\n\
|
|
93
|
-
("a-b 1", ["'a-b'"]),
|
|
95
|
+
("a\tc/d 1", ["line 1", "'c/d'"]),
|
|
96
|
+
("ok 1\n\tk:v 2", ["line 2", "'k:v'"]),
|
|
94
97
|
("~scope\n\tx 1", ["'~scope'"]),
|
|
95
98
|
("log\t@ e", ["'@'"]),
|
|
96
|
-
("a,b
|
|
99
|
+
("a,b#c\tx 1", ["'a,b#c'"]),
|
|
100
|
+
("\"quoted\" 1", ["'\"quoted\"'"]),
|
|
97
101
|
]:
|
|
98
102
|
raises_value_error(lambda: dtab.parse(text), *fragments)
|
|
99
|
-
assert dtab.parse("
|
|
100
|
-
|
|
103
|
+
assert dtab.parse("123aa 1\nfile.json 2\nfile-thing.json 3\n123.json-yaml 4\nitems 5\n_p 6\ncafé 7\n-x 8") == {
|
|
104
|
+
"123aa": "1", "file.json": "2", "file-thing.json": "3", "123.json-yaml": "4", "items": "5", "_p": "6", "café": "7", "-x": "8"}
|
|
105
|
+
for tree in [{"a b": "1"}, {"a,b": "1"}, {"a/b": "1"}, {"": "1"}]:
|
|
101
106
|
raises_value_error(lambda: dtab.stringify(tree), "dtab")
|
|
107
|
+
assert dtab.parse(dtab.stringify({0: "a", "file.json": "b"})) == {"0": "a", "file.json": "b"} # int keys are written as text
|
|
102
108
|
for value in ["x\ny", "x\ty", "a\n\n\tb\n c", "#!/bin/bash\necho hi", ""]:
|
|
103
109
|
assert dtab.parse(dtab.stringify({"a": value})) == {"a": value}, "multiline round trip failed for %r" % value
|
|
104
|
-
|
|
110
|
+
def accepted(probe):
|
|
111
|
+
try:
|
|
112
|
+
dtab.parse(probe + " 1")
|
|
113
|
+
return True
|
|
114
|
+
except ValueError:
|
|
115
|
+
return False
|
|
116
|
+
python_verdicts = [accepted(probe) for probe in IDENTIFIER_PROBES]
|
|
105
117
|
js_verdicts = json.loads(run(
|
|
106
118
|
"node", "-e",
|
|
107
119
|
"const d = require('./dtab.js'); console.log(JSON.stringify(%s.map(p => { try { d.parse(p + ' 1'); return true } catch { return false } })))"
|
|
@@ -159,6 +171,38 @@ def test_vim_tab_key():
|
|
|
159
171
|
assert lines[1] == "after\t 1", repr(lines[1]) # a tab outside the block
|
|
160
172
|
|
|
161
173
|
|
|
174
|
+
def test_vim_shift_keys():
|
|
175
|
+
""">> and << (and visual > <) shift block lines by four spaces, other lines by a tab."""
|
|
176
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
177
|
+
sample = Path(directory) / "shift.dtab"
|
|
178
|
+
sample.write_text("before 1\n$code python\n\tdef f():\n\t return 1\n")
|
|
179
|
+
out = Path(directory) / "lines.txt"
|
|
180
|
+
vim("syntax on", "source dtab.vim", "edit " + str(sample),
|
|
181
|
+
"execute '1normal >>' | execute '1normal <<' | execute '3normal >>' | execute '4normal <<' | execute '2normal Vjj>'",
|
|
182
|
+
"call writefile(getline(1, '$'), '%s')" % out)
|
|
183
|
+
lines = out.read_text().split("\n")
|
|
184
|
+
assert lines[0] == "before 1", repr(lines[0]) # >> gave it a tab, << took it away
|
|
185
|
+
assert lines[1] == "\t$code python", repr(lines[1]) # visual > over the $ line and its block: tabs for all
|
|
186
|
+
assert lines[2] == "\t\t def f():", repr(lines[2]) # >> inside the block: spaces; then the block's tab
|
|
187
|
+
assert lines[3] == "\t\treturn 1", repr(lines[3]) # << inside the block removed the spaces
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_vim_preview():
|
|
191
|
+
""":DtabPreview opens a JSON split of the buffer's tree that follows edits (showing the parser's message when the text is broken), and closes it when repeated."""
|
|
192
|
+
sample = SAMPLES[0]
|
|
193
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
194
|
+
out = Path(directory) / "preview.txt"
|
|
195
|
+
vim("syntax on", "source dtab.vim", "edit " + str(sample),
|
|
196
|
+
"DtabPreview | call writefile(getbufline(b:dtab_preview, 1, '$') + ['---'], '%s')" % out,
|
|
197
|
+
"call setline(1, 'bad/key 1') | doautocmd TextChanged",
|
|
198
|
+
"call writefile(getbufline(b:dtab_preview, 1, '$') + ['---', getbufvar(b:dtab_preview, '&filetype')], '%s', 'a')" % out,
|
|
199
|
+
"DtabPreview | call writefile([bufwinnr(b:dtab_preview)], '%s', 'a')" % out)
|
|
200
|
+
tree, message, closed = out.read_text().split("---\n")
|
|
201
|
+
assert json.loads(tree) == dtab.parse(sample.read_text()), "the preview is not the parsed tree"
|
|
202
|
+
assert message.startswith("dtab line 1: invalid key 'bad/key'"), "the preview did not follow the edit: %r" % message
|
|
203
|
+
assert closed == "json\n-1\n", "expected a json split that the second :DtabPreview closes: %r" % closed
|
|
204
|
+
|
|
205
|
+
|
|
162
206
|
def test_vim_plugin_shim():
|
|
163
207
|
with tempfile.TemporaryDirectory() as directory:
|
|
164
208
|
out = Path(directory) / "filetype.txt"
|
|
@@ -175,15 +219,30 @@ def test_web_demo():
|
|
|
175
219
|
|
|
176
220
|
|
|
177
221
|
def test_vscode_grammar():
|
|
222
|
+
# The committed grammar and language configuration are what vscode/make_grammar.py generates.
|
|
223
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
224
|
+
sys.path.insert(0, str(ROOT / "vscode"))
|
|
225
|
+
import make_grammar # noqa: E402
|
|
226
|
+
for written in make_grammar.write(directory):
|
|
227
|
+
committed = ROOT / "vscode" / Path(written).relative_to(directory)
|
|
228
|
+
assert Path(written).read_text() == committed.read_text(), "%s is stale: run python vscode/make_grammar.py" % committed
|
|
178
229
|
if not (ROOT / "node_modules" / "vscode-textmate").is_dir():
|
|
179
230
|
print(" (skipped: vscode-textmate not installed)")
|
|
180
231
|
return
|
|
181
232
|
subprocess.run(["node", "test/test_vscode.js"], check=True, cwd=ROOT)
|
|
182
233
|
|
|
183
234
|
|
|
235
|
+
def test_vscode_live():
|
|
236
|
+
"""The JSON preview inside the installed VS Code (test/test_vscode_live.js), skipped without @vscode/test-electron or the app."""
|
|
237
|
+
if not (ROOT / "node_modules" / "@vscode" / "test-electron").is_dir() or not Path("/Applications/Visual Studio Code.app").is_dir():
|
|
238
|
+
print(" (skipped: @vscode/test-electron or VS Code not installed)")
|
|
239
|
+
return
|
|
240
|
+
subprocess.run(["node", "test/test_vscode_live.js"], check=True, cwd=ROOT)
|
|
241
|
+
|
|
242
|
+
|
|
184
243
|
if __name__ == "__main__":
|
|
185
244
|
for test in [test_doctests, test_readers_agree, test_round_trips, test_key_rule, test_js_suite,
|
|
186
|
-
test_vim_highlighting, test_vim_embedded_languages, test_vim_tab_key, test_vim_plugin_shim, test_web_demo, test_vscode_grammar]:
|
|
245
|
+
test_vim_highlighting, test_vim_embedded_languages, test_vim_tab_key, test_vim_shift_keys, test_vim_preview, test_vim_plugin_shim, test_web_demo, test_vscode_grammar, test_vscode_live]:
|
|
187
246
|
test()
|
|
188
247
|
print("ok " + test.__name__)
|
|
189
248
|
print("All dtab tests passed")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|