flexdoc 0.1.0__tar.gz → 0.2.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.
- flexdoc-0.2.0/CHANGELOG.md +135 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/PKG-INFO +1 -1
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/flexdoc-spec.md +23 -8
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/specs/active/plan-2026-06-11-flexdoc-extraction.md +6 -2
- flexdoc-0.2.0/docs/project/specs/active/plan-2026-06-13-metrics-use-case.md +531 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/__init__.py +5 -2
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/block_info.py +39 -1
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/block_tree.py +14 -4
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/collect.py +9 -6
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/flex_doc.py +263 -48
- flexdoc-0.2.0/src/flexdoc/docs/links.py +234 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/node.py +1 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/node_table.py +131 -152
- flexdoc-0.2.0/src/flexdoc/docs/sections.py +176 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_collect.py +11 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_flex_doc.py +54 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_links.py +69 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_node.py +2 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_node_table.py +25 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_sections.py +55 -0
- flexdoc-0.2.0/tests/golden/documents/heading_edges.md +36 -0
- flexdoc-0.2.0/tests/golden/documents/inline_pathology.md +19 -0
- flexdoc-0.2.0/tests/golden/documents/link_taxonomy.md +20 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/footnotes_refs/report.yaml +25 -0
- flexdoc-0.2.0/tests/golden/expected/heading_edges/docgraph.yaml +274 -0
- flexdoc-0.2.0/tests/golden/expected/heading_edges/reassembled.md +21 -0
- flexdoc-0.2.0/tests/golden/expected/heading_edges/report.yaml +405 -0
- flexdoc-0.2.0/tests/golden/expected/inline_pathology/docgraph.yaml +91 -0
- flexdoc-0.2.0/tests/golden/expected/inline_pathology/reassembled.md +11 -0
- flexdoc-0.2.0/tests/golden/expected/inline_pathology/report.yaml +283 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/kitchen_sink/report.yaml +14 -13
- flexdoc-0.2.0/tests/golden/expected/link_taxonomy/docgraph.yaml +77 -0
- flexdoc-0.2.0/tests/golden/expected/link_taxonomy/reassembled.md +9 -0
- flexdoc-0.2.0/tests/golden/expected/link_taxonomy/report.yaml +332 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/unicode/report.yaml +1 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/test_golden_docs.py +105 -0
- flexdoc-0.1.0/CHANGELOG.md +0 -66
- flexdoc-0.1.0/src/flexdoc/docs/links.py +0 -109
- flexdoc-0.1.0/src/flexdoc/docs/sections.py +0 -130
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.agents/skills/simple-modern-uv/SKILL.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.agents/skills/simple-modern-uv/references/adopt-existing.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.agents/skills/simple-modern-uv/references/customize.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.agents/skills/simple-modern-uv/references/faq.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.agents/skills/tbd/SKILL.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.claude/.gitignore +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.claude/hooks/tbd-closing-reminder.sh +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.claude/scripts/ensure-gh-cli.sh +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.claude/scripts/tbd-session.sh +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.claude/settings.json +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.claude/skills/tbd/SKILL.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.codex/ensure-gh-cli.sh +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.codex/hooks.json +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.codex/tbd-closing-reminder.sh +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.codex/tbd-session.sh +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.copier-answers.yml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.github/workflows/ci.yml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.github/workflows/publish.yml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.gitignore +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.tbd/.gitattributes +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.tbd/.gitignore +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/.tbd/config.yml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/AGENTS.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/CLAUDE.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/LICENSE +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/Makefile +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/README.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/SUPPLY-CHAIN-SECURITY.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/TODO.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/devtools/lint.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/development.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/installation.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/research/research-2026-05-29-document-model.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/research/research-2026-05-30-multilayer-parsing.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/research/research-2026-05-30-span-references.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/review/senior-engineering-review-flexdoc-standalone-2026-06.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/specs/active/plan-2026-05-29-unified-document-model.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/specs/active/plan-2026-05-31-doc-model-refinements.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/specs/active/plan-2026-05-31-golden-doc-testing.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/specs/active/plan-2026-06-11-structural-metadata.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/publishing.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/docs/usage.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/examples/backfill_timestamps.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/examples/doc_structure.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/examples/normalized_form.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/pyproject.toml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/skills-lock.json +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/__init__.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/base_blocks.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/block_types.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/debug.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/doc_graph.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/doc_graph_schema.json +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/frontmatter.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/interval_index.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/paragraphs.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/render.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/search_tokens.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/sizes.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/span_ref.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/token_diffs.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/token_mapping.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/docs/wordtoks.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/html/__init__.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/html/extractor.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/html/html_in_md.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/html/html_plaintext.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/html/html_tags.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/html/timestamps.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/py.typed +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/util/__init__.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/util/read_time.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/src/flexdoc/util/token_estimate.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/__init__.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/__init__.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_base_blocks.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_block_info.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_block_types.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_blocks.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_caching_threadsafe.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_doc_graph.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_footnote_ref.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_frontmatter.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_interval_index.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_offsets.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_span_ref.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_spans.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_subdoc_and_empty.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_token_diffs.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_token_mapping.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_token_validation.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/docs/test_wordtoks.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/README.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/documents/footnotes_refs.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/documents/inline_html.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/documents/kitchen_sink.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/documents/malformed.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/documents/nested_list.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/documents/tight_vs_loose.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/documents/unicode.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/footnotes_refs/docgraph.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/footnotes_refs/reassembled.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/inline_html/docgraph.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/inline_html/reassembled.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/inline_html/report.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/kitchen_sink/docgraph.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/kitchen_sink/reassembled.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/malformed/docgraph.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/malformed/reassembled.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/malformed/report.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/nested_list/docgraph.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/nested_list/reassembled.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/nested_list/report.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/tight_vs_loose/docgraph.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/tight_vs_loose/reassembled.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/tight_vs_loose/report.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/unicode/docgraph.yaml +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/golden/expected/unicode/reassembled.md +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/html/__init__.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/html/test_html_tag_hardening.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/html/test_html_tags.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/html/test_html_validation_and_classes.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/html/test_timestamps.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/test_examples.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/test_root_api.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/tests/test_supply_chain.py +0 -0
- {flexdoc-0.1.0 → flexdoc-0.2.0}/uv.lock +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to flexdoc are documented here.
|
|
4
|
+
This project uses [semantic versioning](https://semver.org/); while pre-1.0, breaking
|
|
5
|
+
changes bump the **minor** version (see `docs/publishing.md`).
|
|
6
|
+
|
|
7
|
+
## 0.2.0 (2026-06-14)
|
|
8
|
+
|
|
9
|
+
Correctness fixes and a completed inline/heading/link surface for the document-metrics
|
|
10
|
+
use case (`docs/project/specs/active/plan-2026-06-13-metrics-use-case.md`, issue #6).
|
|
11
|
+
As a preview-stage library this takes the cleanest shape with no compatibility shims;
|
|
12
|
+
the API additions below include breaking signature changes (see **Changed**).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **`node_table()` / `collect()` / `graph()` no longer raise on valid Markdown.** Inline
|
|
17
|
+
elements were discovered over the whole source and parented by start offset, so backtick
|
|
18
|
+
pairing across a block boundary (an empty fence next to inline backticks) produced an
|
|
19
|
+
inline span escaping its parent block and raised a layer-nesting error. Inline discovery
|
|
20
|
+
is now scoped per leaf content block, so an inline node can never straddle a block
|
|
21
|
+
boundary; links/images/definitions are parented by full containment.
|
|
22
|
+
- **`sections()` / `toc()` recover every heading `blocks()` finds, and own their content
|
|
23
|
+
correctly.** Headings were re-derived from the blank-line paragraph view, dropping tight
|
|
24
|
+
headings and headings preceded by a non-blank line (e.g. an HTML-comment marker), and
|
|
25
|
+
section content was bucketed from that same view, so a heading glued to its body lost the
|
|
26
|
+
body. Sections now derive entirely from the structural block tree — the heading set *and*
|
|
27
|
+
each section's own content (`own_paragraphs()` / `blocks()` / sizes) come from the section's
|
|
28
|
+
source region — so tight and marker-preceded headings own exactly their content.
|
|
29
|
+
- **Section spans nest correctly** even when a blank-line paragraph straddles a later
|
|
30
|
+
heading (e.g. an embedded `---` block marko reads as a setext heading): each section
|
|
31
|
+
spans from its heading to the next same-or-higher heading (trimmed), which nests by
|
|
32
|
+
construction. Byte-identical to the prior span for well-formed documents.
|
|
33
|
+
- **Reference-definition nodes attach to their block.** A `link_ref_def` span included the
|
|
34
|
+
line's trailing newline and escaped the containing paragraph, leaving the node unparented
|
|
35
|
+
so a block-scoped `collect()` missed it; spans are now trimmed like every structural block.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **Heading metadata on the structural block**: `Block.heading_info` (`HeadingInfo` with
|
|
40
|
+
parser-authoritative `level` and `title`) and the `Block.heading_level` convenience;
|
|
41
|
+
`HeadingInfo` is exported from `flexdoc.docs`. The node table reads heading level from it.
|
|
42
|
+
- **Typed link forms**: `LinkForm` (`inline` / `reference` / `autolink` / `bare_url` /
|
|
43
|
+
`image` / `reference_definition`) and `Link.link_form`. `FlexDoc.links(link_forms=…)` selects any
|
|
44
|
+
forms (default: navigable links only), and `FlexDoc.images()` is a convenience for image
|
|
45
|
+
access. Reference definitions (`[id]: url`) are surfaced as `NodeKind.link_ref_def` nodes
|
|
46
|
+
and via `links(link_forms={LinkForm.reference_definition})`.
|
|
47
|
+
- **`FlexDoc.prose_text()`**: prose-only text for editorial linting and prose metrics —
|
|
48
|
+
prose blocks (paragraphs/headings, and table cells when `include_tables=True`) with inline
|
|
49
|
+
code and footnote refs dropped, links/images replaced by their text/alt, inline-HTML tags
|
|
50
|
+
dropped (wrapped text kept), and heading/blockquote/list markers and reference-definition
|
|
51
|
+
lines stripped; from verbatim source slices (line wrapping preserved, never reflowed) so
|
|
52
|
+
spacing like a spaced em-dash is kept exactly.
|
|
53
|
+
- **`FlexDoc.block_at_offset()`**: the innermost structural `Block` containing an offset
|
|
54
|
+
(the structural counterpart of `paragraph_at_offset`; the name, freed in 0.1.0, now
|
|
55
|
+
correctly returns a `Block`).
|
|
56
|
+
- **Test-suite hardening**: adversarial corpus documents (`inline_pathology`,
|
|
57
|
+
`heading_edges`, `link_taxonomy`); cross-projection invariants tying `toc()` to the
|
|
58
|
+
heading blocks, inline nesting on the query surface, and link-form accounting; and a
|
|
59
|
+
dogfood test that parses every Markdown file in the repo and asserts the invariants. See
|
|
60
|
+
the spec's "Why These Bugs Escaped the Tests" analysis.
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
These are breaking, made cleanly (no aliases) given the preview status:
|
|
65
|
+
|
|
66
|
+
- **`Link` gains a required `link_form: LinkForm` field.** Direct `Link(...)` construction must
|
|
67
|
+
pass it.
|
|
68
|
+
- **`block_links()` returns all link-like constructs** (navigable links, images, and
|
|
69
|
+
reference definitions), each with a `link_form`; previously it returned navigable links only.
|
|
70
|
+
`FlexDoc.links()` filters to navigable links by default, so its default result is
|
|
71
|
+
unchanged.
|
|
72
|
+
- **`collect()` returns inline-kind nodes without `recursive=True`.** An inline-kind
|
|
73
|
+
request (e.g. `collect(kinds={NodeKind.link})`) now widens the candidate set instead of
|
|
74
|
+
silently returning `[]` — matching the documented behavior.
|
|
75
|
+
|
|
76
|
+
## 0.1.0 (2026-06-12)
|
|
77
|
+
|
|
78
|
+
First release.
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- **Initial flexdoc package**, extracted from
|
|
83
|
+
[chopdiff](https://github.com/jlevy/chopdiff) as its own standalone distribution.
|
|
84
|
+
This is the document/markdown layer — `FlexDoc`, paragraphs/sentences, the block tree
|
|
85
|
+
and block types, sections, the node table, `collect()`, `DocGraph`, `SpanRef`, token
|
|
86
|
+
diffs/mappings, word tokenization, html-in-md, and read-time/token estimation — with
|
|
87
|
+
no dependency on chopdiff’s diff and windowed-transform machinery.
|
|
88
|
+
|
|
89
|
+
The import roots are `flexdoc.docs`, `flexdoc.html`, and `flexdoc.util`. Parse
|
|
90
|
+
behavior is unchanged from the `flexdoc.*` modules that previously shipped inside the
|
|
91
|
+
chopdiff wheel; this release packages them independently.
|
|
92
|
+
See `docs/project/specs/active/plan-2026-06-11-flexdoc-extraction.md`.
|
|
93
|
+
|
|
94
|
+
- **A deliberate root API**: the working set is importable from the package root —
|
|
95
|
+
`FlexDoc`, `DocGraph`, `Detail`, `SpanRef`, `BlockType`, `NodeKind`, `Layer`,
|
|
96
|
+
`TextUnit` — designed against the known downstream users and pinned by contract
|
|
97
|
+
tests. The render helpers for source-linked HTML (`render_node_attrs`,
|
|
98
|
+
`wrap_with_node_attrs`, `parse_source_span_attr`) are public in `flexdoc.docs`.
|
|
99
|
+
|
|
100
|
+
- **DocGraph paragraph view**: `Views.paragraphs` joins `toc`/`blocks`/`links`/
|
|
101
|
+
`sentences` in the serialized projection.
|
|
102
|
+
|
|
103
|
+
### Changed (relative to the modules as shipped in chopdiff)
|
|
104
|
+
|
|
105
|
+
The first standalone release also refines the API surface (the pre-publish design
|
|
106
|
+
review, `docs/project/review/senior-engineering-review-flexdoc-standalone-2026-06.md`);
|
|
107
|
+
these are intentional hard cuts with no compatibility aliases:
|
|
108
|
+
|
|
109
|
+
- **`TextDoc` is renamed `FlexDoc`** — the package’s single entry point, named for the
|
|
110
|
+
model it carries (all layered projections hang off it).
|
|
111
|
+
It is importable from the package root: `from flexdoc import FlexDoc`. The module is
|
|
112
|
+
`flexdoc.docs.flex_doc` (was `chopdiff.docs.text_doc`), and the design of record is
|
|
113
|
+
now `docs/flexdoc-spec.md` (was `textdoc-spec.md`).
|
|
114
|
+
- **`collect()` is fully keyword-only and the deprecated aliases are gone**: use
|
|
115
|
+
`subtree_of=` (was `scope=`, previously also positional) and `within=` (was
|
|
116
|
+
`contains=`).
|
|
117
|
+
- **Editing-view methods are named in paragraph terms**, so “block” always means the
|
|
118
|
+
structural layer: `FlexDoc.paragraph_at_offset` (was `block_at_offset`),
|
|
119
|
+
`FlexDoc.iter_paragraphs` (was `iter_blocks`), `Section.own_paragraphs`/
|
|
120
|
+
`subtree_paragraphs` (were `own_blocks`/`subtree_blocks`).
|
|
121
|
+
- **The export surface is settled**: `flexdoc.docs` now exports
|
|
122
|
+
`CodeInfo`/`TableInfo`/`ListInfo`, `resolve`/`resolve_and_update`,
|
|
123
|
+
`parse_blocks`/`walk_blocks`/`block_type_for`, and `DEFAULT_INCLUDE`; `flexdoc.html`
|
|
124
|
+
exports `html_p`, `html_tag`, `escape_attribute`, `tag_wrapper`, and
|
|
125
|
+
`identity_wrapper`. Link extraction is public as `flexdoc.docs.links.block_links`.
|
|
126
|
+
- **`Node.attrs` values are JSON-typed** (`AttrValue`), validated at `DocGraph`
|
|
127
|
+
serialization, and node-id assignment order is pinned and tested for cross-language
|
|
128
|
+
ports; layer nesting guarantees (`LAYER_NESTING`) are enforced at node-table build.
|
|
129
|
+
- Internally, the former `text_doc.py` was split into `flex_doc.py`, `paragraphs.py`,
|
|
130
|
+
`links.py`, and `sections.py` (package imports from `flexdoc.docs` are unaffected),
|
|
131
|
+
and `sections()` is now cached like the other derived views.
|
|
132
|
+
|
|
133
|
+
Migration from chopdiff in one pass: `chopdiff.docs.TextDoc` → `flexdoc.FlexDoc` (or
|
|
134
|
+
`flexdoc.docs.FlexDoc`), `chopdiff.docs.*` → `flexdoc.docs.*`, plus the method renames
|
|
135
|
+
above.
|
|
@@ -365,13 +365,20 @@ is the reassembled working text, and the invariant holds against that.
|
|
|
365
365
|
lazily-cached projections — `blocks()` (§6), `base_blocks()` (§6), `sections()` (§7),
|
|
366
366
|
`links()` (§8), `node_table()` (§4.3), `collect()` (§9), and `graph()` (§10).
|
|
367
367
|
- **Offset inversion:** `paragraph_at_offset(o)` and `sentence_at_offset(o)` map an
|
|
368
|
-
absolute offset back to the editing-view unit containing it
|
|
369
|
-
inter-unit whitespace or
|
|
370
|
-
Structural blocks are addressed by their own spans or via
|
|
368
|
+
absolute offset back to the editing-view unit containing it, and `block_at_offset(o)` to
|
|
369
|
+
the innermost structural `Block` (each `None` for offsets in inter-unit whitespace or
|
|
370
|
+
outside the document). Structural blocks are otherwise addressed by their own spans or via
|
|
371
|
+
`collect(overlaps=...)`.
|
|
371
372
|
- **Frontmatter:** `FlexDoc.frontmatter` is the verbatim leading YAML block or `None`
|
|
372
373
|
(§3).
|
|
373
374
|
- **Sizing:** `size(unit)` and `size_summary()` measure the document in any `TextUnit`
|
|
374
375
|
(see Terminology), including the approximate LLM `tokens` estimate.
|
|
376
|
+
- **Prose projection:** `prose_text(include_tables=False)` returns prose-only text for
|
|
377
|
+
editorial linting and metrics — prose-bearing blocks (paragraphs/headings, and table cells
|
|
378
|
+
when `include_tables=True`) with inline code/footnote refs dropped, links/images reduced to
|
|
379
|
+
their text/alt, inline-HTML tags removed (wrapped text kept), and heading/blockquote/list
|
|
380
|
+
markers and reference-definition lines stripped. Slices are verbatim (line wrapping
|
|
381
|
+
preserved, never reflowed), so editorial spacing (e.g. a spaced em-dash) survives.
|
|
375
382
|
|
|
376
383
|
### 4.3 Nodes, kinds, layers, and the node table
|
|
377
384
|
|
|
@@ -696,12 +703,20 @@ Inline elements (links, code spans, images, inline HTML, footnote references,
|
|
|
696
703
|
`section`/`sentence` associations, so block↔inline relationships are node edges, and
|
|
697
704
|
“links in section 3” is a scoped `collect(kinds={link})`.
|
|
698
705
|
|
|
699
|
-
- `Link(text, url, title, span)`: identity from `flowmark.markdown_ast.extract_links`
|
|
700
|
-
(reference links resolved, escapes honored,
|
|
701
|
-
|
|
706
|
+
- `Link(text, url, title, span, link_form)`: identity from `flowmark.markdown_ast.extract_links`
|
|
707
|
+
(reference links resolved, escapes honored), an AST walk for images, and marko's
|
|
708
|
+
`LinkRefDef` elements for definitions. Each carries a `LinkForm` discriminator — `inline`,
|
|
709
|
+
`reference`, `autolink`, `bare_url`, `image`, or `reference_definition` — so consumers count
|
|
710
|
+
by form without heuristics. flexdoc recovers each exact `[start, end)` by reconciling the
|
|
702
711
|
ordered identities with the name-tagged atomic spans from
|
|
703
|
-
`flowmark.atomic_spans.iter_atomic_spans` (
|
|
704
|
-
|
|
712
|
+
`flowmark.atomic_spans.iter_atomic_spans` (and a trimmed `block_span` for definitions); an
|
|
713
|
+
identity that cannot be located keeps its identity with `span=None`.
|
|
714
|
+
- **`links()` returns navigable links only by default** (`TRUE_LINK_FORMS`: `inline`,
|
|
715
|
+
`reference`, `autolink`, `bare_url`); `links(link_forms=…)` selects any forms and `images()` is
|
|
716
|
+
the convenience for `LinkForm.image`. **Reference definitions** (`[id]: url`) are surfaced
|
|
717
|
+
as `NodeKind.link_ref_def` nodes — parented to their containing block, so a block-scoped
|
|
718
|
+
`collect()` finds them — and via `links(link_forms={LinkForm.reference_definition})`, never the
|
|
719
|
+
default `links()` (a definition is not a link occurrence).
|
|
705
720
|
- `link → sentence` via `sentence_at_offset(link.span[0])`.
|
|
706
721
|
- **`footnote_ref`**: a footnote reference `[^label]` is a first-class inline node
|
|
707
722
|
(`NodeKind.footnote_ref`) carrying its `label` in `attrs` and an exact span, collected
|
{flexdoc-0.1.0 → flexdoc-0.2.0}/docs/project/specs/active/plan-2026-06-11-flexdoc-extraction.md
RENAMED
|
@@ -422,8 +422,12 @@ mechanical) in the same already-breaking release; the migration note is one pass
|
|
|
422
422
|
2026-06-12 (pypi.org returns 404 for `flexdoc`); re-check at publish time.
|
|
423
423
|
Resolve the flexdoc-spec §13 name collision (Stage 2.5 / Open Questions).
|
|
424
424
|
Configure the PyPI Trusted Publisher for `jlevy/flexdoc` (`docs/publishing.md`).
|
|
425
|
-
- [
|
|
426
|
-
|
|
425
|
+
- [x] Tagged and published `flexdoc 0.1.0` (2026-06-12): release-notes PR merged, GitHub
|
|
426
|
+
release v0.1.0 created, `publish.yml` succeeded on its first trusted-publishing run,
|
|
427
|
+
and the package was verified live on PyPI (wheel + sdist) with a clean-venv install
|
|
428
|
+
exercising the root API. Note: the install check must run outside the repo, since the
|
|
429
|
+
project's own cool-off excludes the just-published version.
|
|
430
|
+
|
|
427
431
|
|
|
428
432
|
### Step 5 — rewire chopdiff to the external flexdoc (pending; the breaking release)
|
|
429
433
|
|