How to add language hints to untagged code fences
- Step 1Load the document — Paste the full doc into the box, or switch to Upload file and select one
.mdfile. One document is processed per run. - Step 2Run the tagger — Click Run. There are no settings — the same nine-pattern detection runs every time.
- Step 3Every untagged fence is scanned — The pass finds each bare backtick fence; tilde fences and indented blocks are ignored.
- Step 4Hints are filled in or skipped — Matching blocks get a hint; non-matching blocks and already-tagged blocks are returned as-is.
- Step 5Diff against the original — Compare before and after. Because the transform is deterministic, the only changes are inserted hints on previously bare fences.
- Step 6Save and commit — Download or copy the cleaned Markdown and commit. A later re-run is a no-op on the blocks already tagged.
Cleanup outcomes per fence type
What a single cleanup pass does to each kind of block. The transform only ever adds hints to bare backtick fences.
| Fence in your doc | After the pass |
|---|---|
Bare ``` ```, first line recognised | Hint inserted |
Bare ``` ```, first line not recognised | Left bare (no hint added) |
Already tagged (e.g. ``` yaml ```) | Unchanged |
Tilde fence ~~~ | Skipped — not processed |
| 4-space indented block | Skipped — not a fenced block |
Inline code spans | Untouched — only fenced blocks are scanned |
Detection chain (first match wins)
The fixed order the tagger tests. A block is labelled with the first language whose start-of-block pattern it matches.
| Order | Hint | Recognised opening tokens |
|---|---|---|
| 1 | python | import, from, def , class , if __name__ |
| 2 | javascript | const , let , var , function , =>, import { |
| 3 | rust | pub fn, fn , use , let mut, impl , struct |
| 4 | go | package , import "fmt", func , type , var , const |
| 5 | php | <?php, namespace , echo , function , class |
| 6 | sql | SELECT, INSERT, UPDATE, DELETE, CREATE |
| 7 | html | <html, <div, <span, <!DOCTYPE |
| 8 | json | leading { or [ |
| 9 | cpp | #include, int main, void , std:: |
Cookbook
Cleanup diffs from real docs. Each pair shows the bare input fence and the exact tagged output.
SQL block recovered from a query
The block opens with SELECT (case-insensitive), so it is tagged sql on the sixth pattern.
Input: ``` SELECT id, email FROM users WHERE active = 1; ``` Output: ```sql SELECT id, email FROM users WHERE active = 1; ```
HTML block from a leading tag
A <div start matches the HTML pattern. The match is case-insensitive, so <DIV works too.
Input: ``` <div class="note">Read me first</div> ``` Output: ```html <div class="note">Read me first</div> ```
Bare config block left untouched
A TOML/INI-style block matches nothing in the chain, so the cleanup pass leaves it exactly as found — no false json or text tag.
Input: ``` [server] port = 8080 ``` Output: ``` [server] port = 8080 ```
Mixed doc: only bare fences change
In a doc with one tagged and one bare fence, only the bare one is modified. The tagged block is byte-identical in the output.
Input:
```json
{ "ok": true }
```
```
def run():
return True
```
Output:
```json
{ "ok": true }
```
```python
def run():
return True
```Re-run is a no-op
Running the cleaned doc through the tagger again changes nothing, because every fence now carries a hint.
Input (already cleaned): ```python import os ``` Output: ```python import os ```
Edge cases and what actually happens
Block where nothing matches
Left bareIf the first line does not hit any of the nine patterns — common for YAML, TOML, Bash, CSS, plain logs — the fence is returned bare. This is deliberate: the cleanup never introduces a wrong tag.
Existing tag you disagree with
PreservedThe pass never edits an existing hint. If a contributor tagged a block ``` text and you want bash ```, the tagger leaves it as-is; change it by hand.
Tilde fences in legacy docs
Not detectedOlder docs sometimes use ~~~ fences. These are skipped entirely. Convert them to backticks first if you want them tagged in the same cleanup.
Indented code blocks
Not detectedFour-space-indented blocks are not fenced, so they are ignored. Wrap them in backtick fences before running if they should be tagged.
Comment-first or blank-first block
Left bareDetection reads the top of the block. A block that opens with a comment or blank line usually misses every keyword pattern and stays bare. Reorder so a signature line is first.
Go block tagged javascript
By designJavaScript is checked before Go and both list const /var . A Go block starting with const ( becomes javascript. Lead with package or func for a correct go tag, or fix it manually.
Brace-first block tagged json
By designA block whose first non-space character is { or [ is tagged json. Watch for non-JSON content (e.g. a Rust block opening with a brace) being labelled json.
Very large document
Rejected over limitFree tier accepts up to 500,000 characters and 1 MB per file. A doc beyond that is rejected before the pass runs; split it with md-splitter or use a higher tier (Pro: 5,000,000 characters / 10 MB).
Multiple files to clean
One per runThis tool takes a single document per run. For a folder-wide cleanup, run each file in turn, or combine them with md-merger first — there is no batch mode for this tool.
Frequently asked questions
Does it really tag every untagged block?
It tags every untagged backtick fence whose first line matches one of the nine recognised languages. Blocks that match nothing are left bare on purpose, to avoid introducing wrong tags.
Could it relabel something I already tagged?
No. Existing hints are preserved unchanged. The cleanup only fills blanks; it never edits a fence that already has a language.
What languages are recognised?
Python, JavaScript, Rust, Go, PHP, SQL, HTML, JSON, and C++. YAML, TOML, Bash, CSS, Java, and Ruby are not detected.
Is the result the same every time?
Yes. The transform is deterministic with no options, so the same input always produces the same output — easy to review and safe to automate.
Can I disable detection for one block?
Pre-tag it yourself (e.g. ``` text ```) before running. Tagged blocks are skipped, so an explicit hint keeps the tool from touching it.
Does it handle ~~~ fences?
No. Only backtick (``` ```) fences are processed. Convert tilde fences to backticks first.
What about four-space indented code?
Indented blocks are not fenced and are skipped. Wrap them in backtick fences if you want them tagged.
Will re-running break anything?
No. After the first pass every recognised block is tagged, so a second run is a no-op. That makes it safe in a pre-commit or CI cleanup step.
How big a doc can I clean for free?
Up to 500,000 characters and 1 MB, one file per run. Pro raises this to 5,000,000 characters and 10 MB.
Does it change my repo's language stats?
No. GitHub computes repo language by file extension, not by fence hints. Adding hints only affects how code blocks render.
What other cleanup tools pair with this?
A typical cleanup run is md-prettifier to fix spacing, md-lint to surface structural problems, then this tagger last. Doing the tagger last keeps fence positions stable for the other passes.
Where is my document processed?
In your browser. Nothing is uploaded, which suits internal documentation cleanups that cannot leave your machine.
Privacy first
All Markdown processing runs locally in your browser using JavaScript. No file is ever uploaded to JAD Apps servers — only metadata counters are saved for signed-in dashboard stats.