Code
Use fenced code blocks with a language identifier for syntax highlighting:
```rust
fn main() {
println!("Hello!");
}
```Renders as:
fn main() {
println!("Hello!");
}Supported Languages
Syntax highlighting is powered by Shiki and supports its bundled languages and aliases.
Draftist additionally supports:
- ReScript:
rescript,res, andresi
If a language identifier isn't recognized, Draftist leaves the code readable but unhighlighted. Ping us on Discord if the language you need is missing.
Code Block Features
Add attributes after the language identifier using key=value syntax, separated by spaces:
| Attribute | Description | Example |
|---|---|---|
file | Display a filename badge | file=parser.rs |
highlight | Highlight specific lines | highlight=3,5-8,12 |
caption | Add a description below the block | caption="Error handling logic" |
Filename
Shows a filename badge in the code block header:
```rust file=src/main.rs
fn main() {
let config = Config::load();
run(config);
}
```Renders as:
fn main() {
let config = Config::load();
run(config);
}Line Highlighting
Highlight specific lines to draw attention to important parts:
```rust highlight=3-5
fn validate(input: &str) -> Result<&str, ValidationError> {
let value = input.trim();
if value.is_empty() {
return Err(ValidationError::MissingValue);
}
Ok(value)
}
```Renders as:
fn validate(input: &str) -> Result<&str, ValidationError> {
let value = input.trim();
if value.is_empty() {
return Err(ValidationError::MissingValue);
}
Ok(value)
}| Format | Example | Description |
|---|---|---|
| Single line | highlight=5 | Highlights line 5 |
| Range | highlight=10-15 | Highlights lines 10 through 15 |
| Mixed | highlight=2,5-8,12 | Combines singles and ranges |
For a more convenient option, add a notation comment directly to each line you want to highlight instead of counting line numbers:
```rust
fn validate(input: &str) -> Result<&str, ValidationError> {
let value = input.trim(); // [\!code highlight]
if value.is_empty() { // [\!code highlight:3]
return Err(ValidationError::MissingValue);
}
Ok(value)
}
```Renders as:
fn validate(input: &str) -> Result<&str, ValidationError> {
let value = input.trim(); // [!code highlight]
if value.is_empty() { // [!code highlight:3]
return Err(ValidationError::MissingValue);
}
Ok(value)
}Add a count such as [!code highlight:3] to highlight consecutive lines.
Diffs
Mark removed and added lines with diff notation:
```rust
fn parse_opening(parser: &mut Parser) -> Result<Token> {
parser.stream.expect(TokenKind::LParen) // [\!code --]
parser.expect(TokenKind::LParen) // [\!code ++]
}
```Renders as:
fn parse_opening(parser: &mut Parser) -> Result<Token> {
parser.stream.expect(TokenKind::LParen) // [!code --]
parser.expect(TokenKind::LParen) // [!code ++]
}Focus
Focus a line or region and de-emphasize the surrounding code:
```rust
pub fn expression(parser: &mut Parser) -> Result<Expression> {
let mut left = parser.primary()?;
while let Some(operator) = parser.peek_operator() { // [\!code focus:6]
let precedence = operator.precedence();
parser.advance();
let right = parser.expression_at(precedence)?;
left = Expression::binary(left, operator, right);
}
Ok(left)
}
```Renders as:
pub fn expression(parser: &mut Parser) -> Result<Expression> {
let mut left = parser.primary()?;
while let Some(operator) = parser.peek_operator() { // [!code focus:6]
let precedence = operator.precedence();
parser.advance();
let right = parser.expression_at(precedence)?;
left = Expression::binary(left, operator, right);
}
Ok(left)
}Diagnostics
Use information, warning, and error treatments to distinguish line states:
```rust
fn load_cached(key: &str) -> Result<Value, CacheError> {
match read_cache(key) {
Ok(Some(value)) => Ok(value), // [\!code info]
Ok(None) => Err(CacheError::Missing), // [\!code warning]
Err(error) => Err(CacheError::Unavailable(error)), // [\!code error]
}
}
```Renders as:
fn load_cached(key: &str) -> Result<Value, CacheError> {
match read_cache(key) {
Ok(Some(value)) => Ok(value), // [!code info]
Ok(None) => Err(CacheError::Missing), // [!code warning]
Err(error) => Err(CacheError::Unavailable(error)), // [!code error]
}
}Word Highlights
Highlight a word wherever it appears in the code block:
```rust
fn read_cache(cache: &Cache, key: &str) -> Option<Value> { // [\!code word:entry]
let entry = cache.get(key);
entry.or_else(|| cache.load(key))
}
```Renders as:
fn read_cache(cache: &Cache, key: &str) -> Option<Value> { // [!code word:entry]
let entry = cache.get(key);
entry.or_else(|| cache.load(key))
}Literal Notation
Write [\!code highlight] to display the notation literally. Draftist keeps the comment but displays and copies it without the escape:
/// Add `// [\!code highlight]` after a statement to emphasize it.
pub fn highlight_help() {}Draftist removes notation comments from highlighted code and copied text.
Caption
Captions show up below the code block:
```rust file=parser.rs caption="Recover at the next statement boundary"
fn synchronize(parser: &mut Parser) {
while let Some(token) = parser.peek() {
match token.kind {
TokenKind::Semicolon => {
parser.advance();
return;
}
TokenKind::Fn | TokenKind::Let => return,
_ => parser.advance(),
}
}
}
```Renders as:
fn synchronize(parser: &mut Parser) {
while let Some(token) = parser.peek() {
match token.kind {
TokenKind::Semicolon => {
parser.advance();
return;
}
TokenKind::Fn | TokenKind::Let => return,
_ => parser.advance(),
}
}
}UI Features
Every code block comes with a copy button and a language badge. If the block has a block ID, there's also a link button so readers can grab a direct link to it.