Skip to content

feat(file): let the file name offer a type no content probe can find - #814

Merged
andiwand merged 1 commit into
mainfrom
fix/detect-markdown-by-name
Sep 3, 2026
Merged

feat(file): let the file name offer a type no content probe can find#814
andiwand merged 1 commit into
mainfrom
fix/detect-markdown-by-name

Conversation

@andiwand

@andiwand andiwand commented Sep 3, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Closes #760.

The gap

FileType::markdown was the one document format detection could never reach.
detect_by_content is false for it — correctly, since CommonMark has no
signature and every plain text file is valid markdown — so list_file_types
never offered it and a .md came back as text_file. Worse, a .md that
happened to parse as CSV was offered as CSV.

Meanwhile the name was already at the boundary and thrown away:
abstract::File::disk_path() has carried it all along.

The change

Option (2) from the issue, which turned out to need no new plumbing.
open_strategy::file_type_by_name reads the extension off disk_path() and
returns the type only when the table says no content probe can produce it
(detect_by_content == false). Both detection paths consult it once the bytes
have decoded as text:

  • list_file_types appends it last, so the preference path's reverse tries it
    first — a .md now lists [text_file, …, markdown].
  • open_file tries it ahead of the csv/json/xml probes, so a markdown file
    that parses as CSV stays markdown.

A name never claims bytes: a .md holding a zip is still a zip, and
File::from_memory has no name, so it still needs FileType::markdown.

Verified

New odr.types_md and odr.a_misnamed_file_is_what_its_bytes_are.
FileTypeCapabilities.declaration_matches_the_engines held the old invariant —
"whatever detection sees, the table has to admit to" — and now admits the
name-offered type too; that test failing was the observable change the issue
predicted.

Full suite 1420 passed / 6 pre-existing skips. Reference output byte-identical
(HtmlOutputTests pins as_file_type, so it never went through detection).
Changed TU re-checked under -Werror and clang-tidy.

`FileType::markdown` was unreachable by detection: it has no signature, and a
content probe for it is a probe for "prose with occasional punctuation", so
`detect_by_content` is false and `list_file_types` never offered it. A `.md`
came back as `text_file` and the caller had to already know.

The name is at the boundary and was being thrown away — `abstract::File` already
carries `disk_path`. `file_type_by_name` reads the extension off it and offers
the type when the table says no probe can produce it, once the bytes have
decoded as text. A name only ever adds a candidate: a `.md` holding a zip is
still a zip, and `File::from_memory` has no name, so it still needs
`FileType::markdown`.

Closes #760.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDi21mwuKRGvGiFcGwETtS
@andiwand
andiwand merged commit d60e01e into main Sep 3, 2026
36 checks passed
@andiwand
andiwand deleted the fix/detect-markdown-by-name branch September 3, 2026 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Markdown cannot be detected: reconsider content-only detection, or take the file name as a hint

1 participant