How to heuristic language detection for code blocks
- Step 1Paste the draft — Paste your article with code samples in bare fences, or upload one
.mdfile. One document per run. - Step 2Run detection — Click Run. There are no settings — the heuristic chain runs identically every time.
- Step 3Each block's first line is tested — Detection reads the start of each untagged backtick fence and tries the patterns in order.
- Step 4First match assigns the tag — The earliest matching language wins; unmatched blocks stay bare; tagged blocks are left as-is.
- Step 5Review unusual blocks — Check pseudo-code, regex, and mixed snippets — those are the ones the heuristic intentionally leaves untagged.
- Step 6Correct and re-run as needed — Fix any wrong tag by hand; on the next run the tool sees a tagged block and preserves your edit.
Heuristic detection rules
The complete, explainable rule set. The detector tests these top to bottom against the first line of each untagged block and stops at the first match.
| # | Tag | Matches when the block starts with |
|---|---|---|
| 1 | python | import, from, def , class , or if __name__ |
| 2 | javascript | const , let , var , function , =>, or import { |
| 3 | rust | pub fn, fn , use , let mut, impl , or struct |
| 4 | go | package , import "fmt", func , type , var , or const |
| 5 | php | <?php, namespace , echo , function , or class |
| 6 | sql | SELECT/INSERT/UPDATE/DELETE/CREATE (any case) |
| 7 | html | <html/<div/<span/<!DOCTYPE (any case) |
| 8 | json | first non-space char is { or [ |
| 9 | cpp | #include, int main, void , or std:: |
Content the heuristic deliberately skips
These never match a rule and are left untagged — by design, to avoid wrong labels in published writing.
| Snippet type | Why it is skipped |
|---|---|
| Pseudo-code | No language keywords to match |
| Regular expressions | No regex rule exists |
| Bash / shell commands | Shell is not in the rule set |
| YAML / TOML / INI config | Not covered by any pattern |
| TypeScript-only syntax | No typescript rule (may match javascript) |
| Comment-first or blank-first blocks | Detection reads the top line, which is not a signature |
Cookbook
Worked detections for a tech-writing audience. Each shows the bare input and the exact tagged output.
Python from a class definition
The block opens with class , the Python pattern, so it is tagged python.
Input:
```
class Parser:
def parse(self, text):
return text.strip()
```
Output:
```python
class Parser:
def parse(self, text):
return text.strip()
```SQL from a CREATE statement
CREATE matches case-insensitively, tagging the schema example sql.
Input: ``` CREATE TABLE posts (id INT PRIMARY KEY, title TEXT); ``` Output: ```sql CREATE TABLE posts (id INT PRIMARY KEY, title TEXT); ```
Pseudo-code left untagged
Pseudo-code uses no real keywords, so no rule fires and the block stays bare — exactly what you want in an explanatory article.
Input:
```
for each item in cart:
add item.price to total
```
Output:
```
for each item in cart:
add item.price to total
```Regex block stays bare
There is no regex rule, so a pattern block is left untagged. Tag it ``` regex ``` yourself if your renderer supports it.
Input:
```
^\d{4}-\d{2}-\d{2}$
```
Output:
```
^\d{4}-\d{2}-\d{2}$
```Order decides an ambiguous block
This block opens with const, which JavaScript (rule 2) tests before Go (rule 4) — so it is tagged javascript even though it is valid Go.
Input:
```
const Pi = 3.14159
func area(r float64) float64 { return Pi*r*r }
```
Output:
```javascript
const Pi = 3.14159
func area(r float64) float64 { return Pi*r*r }
```Edge cases and what actually happens
Pseudo-code
Left bareExplanatory pseudo-code rarely contains real keywords, so no rule matches and the block stays untagged. That is the intended behaviour — a wrong tag would be worse than none in published writing.
Regular-expression block
Left bareThere is no regex rule. A pattern block is left untagged; tag it ``` regex ``` by hand if your renderer has a regex component.
TypeScript-only syntax
By designNo typescript rule exists. A TS snippet that opens with const/function/import { is tagged javascript; one that opens with TS-only syntax may match nothing and stay bare.
First line is a comment
Left bareDetection reads the top line. A block opening with #, //, or /* usually misses every keyword pattern and stays bare. Put a signature line first for a reliable hit.
Ambiguous keyword across languages
By designconst , var , function , and class appear in more than one rule. The first rule in order wins, so a block may be tagged the earlier language (often javascript or python). Reorder the snippet or fix the tag.
Brace-first block
By designA block whose first character is { or [ is tagged json (rule 8). Non-JSON content opening with a brace can be mislabelled — review such blocks.
Already-tagged or corrected block
PreservedAny block with an existing hint — including a tag you fixed by hand on a previous pass — is left unchanged. Re-running a growing draft is safe.
Tilde or indented snippets
Not detectedOnly backtick fences are scanned. Tilde (~~~) fences and four-space-indented code in an article are skipped.
Long-form article over the limit
Rejected over limitFree tier accepts up to 500,000 characters / 1 MB per run. A book-length manuscript beyond that is rejected; split it with md-splitter or use a higher tier (Pro: 5,000,000 / 10 MB).
Frequently asked questions
How does it handle pseudo-code?
Pseudo-code rarely matches a rule, so it is left untagged. The heuristic prefers leaving a block bare over guessing a wrong language.
What about regex blocks?
There is no regex rule, so regex blocks stay bare. Tag them ``` regex ``` manually if your renderer supports a regex component.
Can detection improve or learn over time?
No. The rules are a fixed, deterministic chain with no model or training. The same input always yields the same output.
Which languages does the heuristic cover?
Python, JavaScript, Rust, Go, PHP, SQL, HTML, JSON, and C++ — nine in total. Others are intentionally left untagged.
How does it pick between two possible languages?
It tests rules in a fixed order (Python first, C++ last) and stops at the first match. Shared keywords like const resolve to the earlier rule.
Why was a valid Go block tagged javascript?
JavaScript (rule 2) is tested before Go (rule 4) and both list const /var . Lead the block with package or func to get go, or correct the tag manually. Detection keys off the first line, so a clear signature line at the top gives the most reliable result.
Does it detect TypeScript?
Not as typescript. TS that opens with JS-like syntax is tagged javascript; TS-only syntax may not match at all. Rename by hand if needed.
Is it safe to re-run on a draft I'm still editing?
Yes. It is idempotent — tagged blocks (including ones you corrected) are preserved, so re-running only touches new bare fences.
Does it support tilde fences or indented code?
No. Only backtick (``` ```) fences are detected. Convert tilde fences or fence indented code first.
How long an article can I process for free?
Up to 500,000 characters / 1 MB per run. Pro raises this to 5,000,000 characters / 10 MB. Past that, split a manuscript with md-splitter.
What pairs well with tagging in a writing workflow?
Tidy the draft with md-prettifier before tagging, and if you are moving GitHub-flavoured Markdown to a CommonMark renderer, run md-gfm-to-commonmark first so fences are consistent.
Is my draft sent anywhere?
No. Detection runs entirely in your browser, so unpublished drafts stay on 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.