nvim-doc-reader 0.1.0__tar.gz → 0.1.1__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.
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/PKG-INFO +1 -1
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/pyproject.toml +1 -1
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/model.py +13 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/skills/doc-authoring/SKILL.md +13 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/static/reader.css +32 -2
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/static/reader.js +10 -1
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/PKG-INFO +1 -1
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/tests/test_model.py +16 -2
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/tests/test_nvim.py +16 -12
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/tests/test_serve.py +10 -4
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/tests/test_skill.py +9 -3
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/tests/test_urls_config.py +2 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/README.md +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/setup.cfg +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/__init__.py +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/cli.py +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/config.py +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/nvim/lua/doc_reader.lua +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/nvim.py +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/serve.py +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/urls.py +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/SOURCES.txt +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/dependency_links.txt +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/entry_points.txt +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/requires.txt +0 -0
- {nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nvim-doc-reader"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "Keyboard-driven HTML reader for Markdown docs, with jumps into running Neovim"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -83,6 +83,8 @@ def make_markdown():
|
|
|
83
83
|
md.add_render_rule("link_open", _link_open)
|
|
84
84
|
md.add_render_rule("image", _image)
|
|
85
85
|
md.add_render_rule("list_item_open", _list_item_open)
|
|
86
|
+
md.add_render_rule("table_open", _table_open)
|
|
87
|
+
md.add_render_rule("table_close", _table_close)
|
|
86
88
|
return md
|
|
87
89
|
|
|
88
90
|
|
|
@@ -277,5 +279,16 @@ def _list_item_open(self, tokens, idx, options, env):
|
|
|
277
279
|
return self.renderToken(tokens, idx, options, env)
|
|
278
280
|
|
|
279
281
|
|
|
282
|
+
# The wrapper is what reader.css widens past the text column and scrolls when
|
|
283
|
+
# the table outgrows the screen; tabindex lets the arrow keys scroll it.
|
|
284
|
+
def _table_open(self, tokens, idx, options, env):
|
|
285
|
+
return ('<div class="dr-table" tabindex="0">'
|
|
286
|
+
+ self.renderToken(tokens, idx, options, env))
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _table_close(self, tokens, idx, options, env):
|
|
290
|
+
return self.renderToken(tokens, idx, options, env) + "</div>\n"
|
|
291
|
+
|
|
292
|
+
|
|
280
293
|
# After the render rules it refers to.
|
|
281
294
|
_MD = make_markdown()
|
{nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/doc_reader/skills/doc-authoring/SKILL.md
RENAMED
|
@@ -42,6 +42,19 @@ Add them where they help understanding, not for decoration.
|
|
|
42
42
|
- Reference assets with relative paths, give images alt text, and say in the
|
|
43
43
|
prose what the figure shows.
|
|
44
44
|
|
|
45
|
+
## Tables
|
|
46
|
+
|
|
47
|
+
A table is for comparing a few things on a few measures; a dataframe dump is
|
|
48
|
+
not a table.
|
|
49
|
+
|
|
50
|
+
- About 8 columns at most. More than that: transpose (measures as rows), split
|
|
51
|
+
it by topic, or keep the full data as a CSV in `figures/` and show only the
|
|
52
|
+
columns that make the point.
|
|
53
|
+
- The first column names the row; the reader keeps it in view when a wide
|
|
54
|
+
table scrolls.
|
|
55
|
+
- Units in the header (`pnl $k`, `lat us`), not in every cell; round to the
|
|
56
|
+
digits that matter; right-align numeric columns with `---:`.
|
|
57
|
+
|
|
45
58
|
## Honesty
|
|
46
59
|
|
|
47
60
|
- Never fabricate empirical results. Numbers come from a run you can point
|
|
@@ -106,8 +106,38 @@ pre { background: var(--code-bg); padding: 12px 14px; border-radius: 6px; overfl
|
|
|
106
106
|
pre code { background: none; padding: 0; }
|
|
107
107
|
pre.mermaid { background: none; text-align: center; }
|
|
108
108
|
blockquote { border-left: 3px solid var(--rule); margin-left: 0; padding-left: 14px; color: var(--dim); }
|
|
109
|
-
table
|
|
110
|
-
|
|
109
|
+
/* A table as wide as it needs, up to the screen: narrow ones stay left in the
|
|
110
|
+
column, wide ones grow out of it centred on it. One wider than the screen or
|
|
111
|
+
taller than 80vh scrolls inside the box, which keeps its header row and
|
|
112
|
+
first column in view. Separate borders: collapsed ones scroll off sticky cells,
|
|
113
|
+
and backgrounds clipped to the padding so a sticky cell's cannot cover its
|
|
114
|
+
neighbour's border. */
|
|
115
|
+
.dr-table {
|
|
116
|
+
width: max-content;
|
|
117
|
+
min-width: 100%;
|
|
118
|
+
max-width: calc(100vw - 48px);
|
|
119
|
+
max-height: 80vh;
|
|
120
|
+
overflow: auto;
|
|
121
|
+
position: relative;
|
|
122
|
+
left: 50%;
|
|
123
|
+
transform: translateX(-50%);
|
|
124
|
+
margin: 0 0 1em;
|
|
125
|
+
}
|
|
126
|
+
@media (min-width: 1180px) {
|
|
127
|
+
body.show-outline .dr-table { max-width: calc(100vw - var(--outline-w) - 48px); }
|
|
128
|
+
}
|
|
129
|
+
table { border-collapse: separate; border-spacing: 0; font-size: 14px; }
|
|
130
|
+
.dr-table table { margin: 0; }
|
|
131
|
+
th, td { border: solid var(--rule); border-width: 0 1px 1px 0; padding: 5px 10px;
|
|
132
|
+
text-align: left; font-variant-numeric: tabular-nums;
|
|
133
|
+
background: var(--bg); background-clip: padding-box; }
|
|
134
|
+
tr > :first-child { border-left-width: 1px; }
|
|
135
|
+
thead tr:first-child > * { border-top-width: 1px; }
|
|
136
|
+
/* Row labels and numbers stay on one line; headers and prose cells wrap. */
|
|
137
|
+
td[style*="right"], .dr-table tr > :first-child { white-space: nowrap; }
|
|
138
|
+
.dr-table thead th { position: sticky; top: 0; z-index: 1; background: var(--code-bg); }
|
|
139
|
+
.dr-table tr > :first-child { position: sticky; left: 0; }
|
|
140
|
+
.dr-table thead th:first-child { z-index: 2; }
|
|
111
141
|
img { max-width: 100%; height: auto; }
|
|
112
142
|
div.math { overflow-x: auto; margin: 0 0 1em; }
|
|
113
143
|
ul.listing { list-style: none; padding: 0; }
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
return false;
|
|
28
28
|
}
|
|
29
29
|
const visible = () => sections().filter((s) => !hidden(s));
|
|
30
|
+
const tables = () => [...docEl.querySelectorAll(".dr-table")];
|
|
30
31
|
// Section IDs come from the document, so look only among its sections: a
|
|
31
32
|
// heading may be called anything, including what the page itself uses.
|
|
32
33
|
const byId = (id) =>
|
|
@@ -378,7 +379,11 @@
|
|
|
378
379
|
return { collapsed: sections().filter(collapsed).map((s) => s.id),
|
|
379
380
|
cur: cur && cur.id,
|
|
380
381
|
offset: cur ? heading(cur).getBoundingClientRect().top : null,
|
|
381
|
-
scrollY: window.scrollY
|
|
382
|
+
scrollY: window.scrollY,
|
|
383
|
+
// Matched back by position: an edit rarely adds a table above
|
|
384
|
+
// the one being read.
|
|
385
|
+
tables: tables().map((t) => [t.scrollLeft, t.scrollTop]),
|
|
386
|
+
focusedTable: tables().indexOf(document.activeElement) };
|
|
382
387
|
}
|
|
383
388
|
function applyState(state) {
|
|
384
389
|
(state.collapsed || []).forEach((id) => { const s = byId(id); if (s && s.tagName === "SECTION") s.classList.add("collapsed"); });
|
|
@@ -438,6 +443,10 @@
|
|
|
438
443
|
renderMath(docEl);
|
|
439
444
|
await renderMermaid(docEl);
|
|
440
445
|
if (my !== gen) return true;
|
|
446
|
+
tables().forEach((t, i) => {
|
|
447
|
+
if (snap.tables[i]) [t.scrollLeft, t.scrollTop] = snap.tables[i];
|
|
448
|
+
if (i === snap.focusedTable) t.focus({ preventScroll: true });
|
|
449
|
+
});
|
|
441
450
|
if (cur && snap.offset !== null) {
|
|
442
451
|
window.scrollTo({ top: window.scrollY + heading(cur).getBoundingClientRect().top - snap.offset });
|
|
443
452
|
} else {
|
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
The page, `docread url` and the Neovim jump all locate sections through this
|
|
4
4
|
one parse, so these pin the cases where a naive heading scan would disagree
|
|
5
5
|
with the rendered page."""
|
|
6
|
-
|
|
7
|
-
import
|
|
6
|
+
import pytest
|
|
7
|
+
import utils.doc_reader_src # noqa: F401 (puts doc_reader on sys.path)
|
|
8
|
+
|
|
9
|
+
# The parser needs mdit-py-plugins, which is newer than some container images.
|
|
10
|
+
pytest.importorskip("mdit_py_plugins")
|
|
11
|
+
|
|
12
|
+
from doc_reader import model # noqa: E402
|
|
13
|
+
import re # noqa: E402
|
|
14
|
+
from importlib import resources # noqa: E402
|
|
8
15
|
|
|
9
16
|
|
|
10
17
|
def ids(text):
|
|
@@ -86,6 +93,13 @@ def test_raw_html_is_escaped_and_mermaid_kept_as_source():
|
|
|
86
93
|
assert '<pre class="mermaid">A --> B\n</pre>' in html
|
|
87
94
|
|
|
88
95
|
|
|
96
|
+
def test_the_table_wrapper_is_the_one_reader_css_styles():
|
|
97
|
+
html = model.render(model.parse("| a | n |\n|---|---:|\n| x | 1 |\n"))
|
|
98
|
+
cls = re.search(r'<div class="([\w-]+)"[^>]*><table>', html).group(1)
|
|
99
|
+
css = (resources.files("doc_reader") / "static" / "reader.css").read_text()
|
|
100
|
+
assert re.search(rf"^\.{cls} \{{", css, re.M)
|
|
101
|
+
|
|
102
|
+
|
|
89
103
|
def test_project_task_rows_link_by_their_source_number():
|
|
90
104
|
text = "# P\n\n3. [t] three\n7. [ ] seven\n9. [s] nine\n"
|
|
91
105
|
html = model.render(model.parse(text),
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
"""`n` against real headless Neovim instances: which ones move, where they
|
|
2
2
|
land, and that the target is found in the editor's text, not the disk's."""
|
|
3
|
-
from doc_reader import config, nvim
|
|
4
|
-
import asyncio
|
|
5
|
-
import json
|
|
6
|
-
import os
|
|
7
|
-
import shutil
|
|
8
|
-
import socket
|
|
9
|
-
import subprocess
|
|
10
|
-
import sys
|
|
11
|
-
import tempfile
|
|
12
|
-
import time
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
|
|
15
3
|
import pytest
|
|
4
|
+
import utils.doc_reader_src # noqa: F401 (puts doc_reader on sys.path)
|
|
5
|
+
|
|
6
|
+
# The parser needs mdit-py-plugins, which is newer than some container images.
|
|
7
|
+
pytest.importorskip("mdit_py_plugins")
|
|
8
|
+
|
|
9
|
+
from doc_reader import config, nvim # noqa: E402
|
|
10
|
+
import asyncio # noqa: E402
|
|
11
|
+
import json # noqa: E402
|
|
12
|
+
import os # noqa: E402
|
|
13
|
+
import shutil # noqa: E402
|
|
14
|
+
import socket # noqa: E402
|
|
15
|
+
import subprocess # noqa: E402
|
|
16
|
+
import sys # noqa: E402
|
|
17
|
+
import tempfile # noqa: E402
|
|
18
|
+
import time # noqa: E402
|
|
19
|
+
from pathlib import Path # noqa: E402
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
pytestmark = pytest.mark.skipif(shutil.which("nvim") is None, reason="no nvim")
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
"""The server's URL handling end to end: every file has one URL, and asking
|
|
2
2
|
for any other spelling of it lands there in one hop."""
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import pytest
|
|
4
|
+
import utils.doc_reader_src # noqa: F401 (puts doc_reader on sys.path)
|
|
5
|
+
|
|
6
|
+
# The parser needs mdit-py-plugins, which is newer than some container images.
|
|
7
|
+
pytest.importorskip("mdit_py_plugins")
|
|
8
|
+
|
|
9
|
+
from doc_reader import config, serve # noqa: E402
|
|
10
|
+
from aiohttp.test_utils import TestClient, TestServer # noqa: E402
|
|
11
|
+
import asyncio # noqa: E402
|
|
12
|
+
import json # noqa: E402
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
def fetch(cfg, *urls):
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"""The bundled agent skill: what `install-skill` writes is what the README
|
|
2
2
|
tells people to reference, under the name Claude Code will look it up by."""
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
3
|
+
import pytest
|
|
4
|
+
import utils.doc_reader_src # noqa: F401 (puts doc_reader on sys.path)
|
|
5
|
+
|
|
6
|
+
# The parser needs mdit-py-plugins, which is newer than some container images.
|
|
7
|
+
pytest.importorskip("mdit_py_plugins")
|
|
8
|
+
|
|
9
|
+
from doc_reader import cli # noqa: E402
|
|
10
|
+
import os # noqa: E402
|
|
11
|
+
import re # noqa: E402
|
|
6
12
|
|
|
7
13
|
|
|
8
14
|
README = os.path.join(os.path.dirname(__file__), "..", "README.md")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{nvim_doc_reader-0.1.0 → nvim_doc_reader-0.1.1}/src/nvim_doc_reader.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|