Implement blockquote token
This commit is contained in:
parent
aa41e33ced
commit
260610c4a0
11 changed files with 263 additions and 120 deletions
|
|
@ -18,7 +18,7 @@ alias r := run
|
|||
# Build and serve on changes
|
||||
[group: 'develop']
|
||||
run-watch:
|
||||
{{ watch_cmd }} {{ just_cmd }} run
|
||||
@{{ watch_cmd }} {{ just_cmd }} run
|
||||
|
||||
alias w := run-watch
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ doc_broken_link = "warn"
|
|||
doc_comment_double_space_linebreaks = "warn"
|
||||
doc_link_with_quotes = "warn"
|
||||
doc_markdown = "warn"
|
||||
empty_enum = "warn"
|
||||
empty_enums = "warn"
|
||||
expl_impl_clone_on_copy = "warn"
|
||||
explicit_deref_methods = "warn"
|
||||
explicit_into_iter_loop = "warn"
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
use crate::syntax::content::parser::{
|
||||
State, Token,
|
||||
token::{Header, Paragraph, PreFormat, Quote, Verse},
|
||||
token::{Header, Paragraph, PreFormat, Verse},
|
||||
};
|
||||
|
||||
pub mod block;
|
||||
pub mod inline;
|
||||
pub mod anchor;
|
||||
pub mod list;
|
||||
pub mod quote;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Context {
|
||||
pub block: Block,
|
||||
pub inline: Inline,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub enum Block {
|
||||
Paragraph,
|
||||
Header(u8), // level
|
||||
|
|
@ -22,13 +23,15 @@ pub enum Block {
|
|||
PreFormat,
|
||||
Quote,
|
||||
Verse,
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub enum Inline {
|
||||
Anchor,
|
||||
Code,
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +52,7 @@ pub fn close(state: &State, tokens: &mut Vec<Token>) {
|
|||
tokens.push(Token::Header(Header::from_u8(level, false, None)));
|
||||
},
|
||||
Block::Quote => {
|
||||
tokens.push(Token::Quote(Quote::new(false)));
|
||||
panic!("End of input with open quote")
|
||||
},
|
||||
Block::Verse => {
|
||||
tokens.push(Token::Verse(Verse::new(false)));
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ pub fn parse(
|
|||
} else if Quote::probe(lexeme) {
|
||||
log!(VERBOSE, "Block Context: None -> Quote on {lexeme}");
|
||||
state.context.block = Block::Quote;
|
||||
tokens.push(Token::Quote(Quote::new(true)));
|
||||
iterator.next();
|
||||
return true;
|
||||
} else if Verse::probe(lexeme) {
|
||||
log!(VERBOSE, "Block Context: None -> Verse on {lexeme}");
|
||||
|
|
@ -93,15 +93,7 @@ pub fn parse(
|
|||
return super::list::parse(lexeme, state, tokens, iterator, graph);
|
||||
},
|
||||
Block::Quote => {
|
||||
if lexeme.match_char_sequence('\n', '>') {
|
||||
tokens.push(Token::LineBreak(LineBreak::default()));
|
||||
iterator.next();
|
||||
return true;
|
||||
} else if Quote::probe_end(lexeme) {
|
||||
tokens.push(Token::Quote(Quote::new(false)));
|
||||
log!(VERBOSE, "Block Context: Quote -> None on {lexeme}");
|
||||
state.context.block = Block::None;
|
||||
}
|
||||
return super::quote::parse(lexeme, state, tokens, iterator, graph);
|
||||
},
|
||||
Block::Verse => {
|
||||
if Verse::probe_end(lexeme) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ pub fn parse(
|
|||
let candidate = &mut buffer.candidate;
|
||||
let item_candidate = &mut buffer.item_candidate;
|
||||
|
||||
#[allow(clippy::wildcard_enum_match_arm)]
|
||||
match state.context.block {
|
||||
Block::List => {
|
||||
if lexeme.match_char(' ') && item_candidate.depth.is_none() {
|
||||
|
|
|
|||
97
src/syntax/content/parser/context/quote.rs
Normal file
97
src/syntax/content/parser/context/quote.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
use std::{iter::Peekable, slice::Iter};
|
||||
|
||||
use crate::{
|
||||
graph::Graph,
|
||||
prelude::*,
|
||||
syntax::content::parser::{
|
||||
Lexeme, State, Token,
|
||||
context::Block,
|
||||
format, state,
|
||||
token::{Anchor, Quote},
|
||||
},
|
||||
};
|
||||
|
||||
/// Handles open quote contexts until a quote is fully parsed.
|
||||
///
|
||||
/// A return of `true` will trigger a continue in the outer parser,
|
||||
/// skipping any further parsing of the current lexeme.
|
||||
///
|
||||
/// # Panics
|
||||
/// This parser can handle only the Quote context, and will panic if passed an
|
||||
/// unrelated context since it has no knowledge on how to handle them.
|
||||
pub fn parse(
|
||||
lexeme: &Lexeme,
|
||||
state: &mut State,
|
||||
tokens: &mut Vec<Token>,
|
||||
iterator: &mut Peekable<Iter<'_, Lexeme>>,
|
||||
graph: &Graph,
|
||||
) -> bool {
|
||||
let buffer = &mut state.buffers.quote;
|
||||
let candidate = &mut buffer.candidate;
|
||||
|
||||
#[allow(clippy::wildcard_enum_match_arm)]
|
||||
match state.context.block {
|
||||
Block::Quote => {
|
||||
if Quote::probe_end(lexeme) {
|
||||
log!("Probed end of quote on {lexeme}");
|
||||
let (text, text_tokens) = format(&candidate.text, graph);
|
||||
candidate.text = text;
|
||||
state.format_tokens.extend_from_slice(&text_tokens);
|
||||
|
||||
if let Some(citation) = &candidate.citation {
|
||||
let (formatted_citation, citation_tokens) =
|
||||
format(citation, graph);
|
||||
candidate.citation = Some(formatted_citation);
|
||||
state.format_tokens.extend_from_slice(&citation_tokens);
|
||||
|
||||
let mut first_anchor = Anchor::default();
|
||||
for token in citation_tokens {
|
||||
if let Token::Anchor(token_data) = token {
|
||||
first_anchor = *token_data.clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if first_anchor.external() {
|
||||
candidate.url = first_anchor.destination();
|
||||
}
|
||||
}
|
||||
|
||||
tokens.push(Token::Quote(candidate.clone()));
|
||||
log!(VERBOSE, "Block Context: Quote -> None on {lexeme}");
|
||||
state.context.block = Block::None;
|
||||
*buffer = state::QuoteBuffer::default();
|
||||
} else if !buffer.in_citation
|
||||
&& lexeme.match_char('\n')
|
||||
&& lexeme.next() == "--"
|
||||
{
|
||||
log!("Matched citation start on {lexeme}");
|
||||
buffer.in_citation = true;
|
||||
iterator.next();
|
||||
iterator.next();
|
||||
} else if lexeme.match_char_sequence('\n', '>') {
|
||||
log!("Matched break-aware sequence on {lexeme}");
|
||||
candidate.text.push_str(" <\n");
|
||||
iterator.next();
|
||||
} else {
|
||||
log!("Entered quote else branch on {lexeme}");
|
||||
if buffer.in_citation {
|
||||
log!("Extending citation on {lexeme}");
|
||||
candidate.extend_citation(&lexeme.text());
|
||||
if lexeme.match_char('\n') && lexeme.next() == "--" {
|
||||
candidate.text.push('\n');
|
||||
iterator.next();
|
||||
} else if lexeme.match_char('\n') {
|
||||
buffer.in_citation = false;
|
||||
}
|
||||
} else {
|
||||
log!("Extending quote on {lexeme}");
|
||||
candidate.text.push_str(&lexeme.text());
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("Quote context parser called to handle non-quote context")
|
||||
},
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@ use std::collections::HashMap;
|
|||
|
||||
use crate::syntax::content::parser::{
|
||||
Token,
|
||||
context::{Block, Context, Inline},
|
||||
token::{Anchor, Item, List},
|
||||
context::Context,
|
||||
token::{Anchor, Item, List, Quote},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct State {
|
||||
pub context: Context,
|
||||
pub dom_ids: HashMap<String, Vec<String>>,
|
||||
|
|
@ -15,7 +15,7 @@ pub struct State {
|
|||
pub format_tokens: Vec<Token>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Switches {
|
||||
pub bold: bool,
|
||||
pub oblique: bool,
|
||||
|
|
@ -23,10 +23,11 @@ pub struct Switches {
|
|||
pub underline: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Buffers {
|
||||
pub anchor: AnchorBuffer,
|
||||
pub list: ListBuffer,
|
||||
pub quote: QuoteBuffer,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
|
|
@ -43,6 +44,12 @@ pub struct AnchorBuffer {
|
|||
pub destination: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct QuoteBuffer {
|
||||
pub candidate: Quote,
|
||||
pub in_citation: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AnchorBuffer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let display_text = if self.text.is_empty() {
|
||||
|
|
@ -67,37 +74,6 @@ impl std::fmt::Display for AnchorBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> State {
|
||||
State {
|
||||
context: Context {
|
||||
inline: Inline::None,
|
||||
block: Block::None,
|
||||
},
|
||||
dom_ids: HashMap::default(),
|
||||
switches: Switches {
|
||||
bold: false,
|
||||
crossout: false,
|
||||
oblique: false,
|
||||
underline: false,
|
||||
},
|
||||
buffers: Buffers {
|
||||
anchor: AnchorBuffer {
|
||||
candidate: Anchor::default(),
|
||||
text: String::default(),
|
||||
destination: String::default(),
|
||||
},
|
||||
list: ListBuffer {
|
||||
candidate: List::default(),
|
||||
item_candidate: Item::default(),
|
||||
depth: 0,
|
||||
},
|
||||
},
|
||||
format_tokens: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,24 @@
|
|||
use crate::syntax::content::{Parseable, parser::Lexeme};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct Quote {
|
||||
open: Option<bool>,
|
||||
pub text: String,
|
||||
pub citation: Option<String>,
|
||||
pub url: Option<String>,
|
||||
}
|
||||
|
||||
impl Quote {
|
||||
pub fn new(open: bool) -> Quote {
|
||||
Quote { open: Some(open) }
|
||||
}
|
||||
|
||||
pub fn probe_end(lexeme: &Lexeme) -> bool {
|
||||
lexeme.match_char_sequence('\n', '\n')
|
||||
}
|
||||
|
||||
pub fn extend_citation(&mut self, s: &str) {
|
||||
if let Some(current) = &self.citation {
|
||||
self.citation = Some(format!("{current}{s}"));
|
||||
} else {
|
||||
self.citation = Some(String::from(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parseable for Quote {
|
||||
|
|
@ -21,19 +27,26 @@ impl Parseable for Quote {
|
|||
}
|
||||
|
||||
fn lex(_lexeme: &Lexeme) -> Quote {
|
||||
Quote { open: None }
|
||||
Quote::default()
|
||||
}
|
||||
|
||||
fn render(&self) -> String {
|
||||
if let Some(open) = self.open {
|
||||
if open {
|
||||
"<blockquote>".to_owned()
|
||||
} else {
|
||||
"</blockquote>".to_owned()
|
||||
}
|
||||
let opening = if let Some(url) = &self.url {
|
||||
format!(r#"<blockquote cite="{url}">"#)
|
||||
} else {
|
||||
panic!("Attempt to render a quote tag while open state is unknown")
|
||||
}
|
||||
String::from("<blockquote>")
|
||||
};
|
||||
|
||||
let content = if let Some(citation) = &self.citation {
|
||||
format!(
|
||||
r#"{}<br/><p class="quote-citation">{citation}</p>"#,
|
||||
&self.text
|
||||
)
|
||||
} else {
|
||||
String::from(&self.text)
|
||||
};
|
||||
|
||||
format!("\n{opening}\n{content}\n</blockquote>\n")
|
||||
}
|
||||
|
||||
fn flatten(&self) -> String {
|
||||
|
|
@ -43,16 +56,14 @@ impl Parseable for Quote {
|
|||
|
||||
impl std::fmt::Display for Quote {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let display_open_state = match self.open {
|
||||
Some(open_state) => {
|
||||
if open_state {
|
||||
"open"
|
||||
} else {
|
||||
"closed"
|
||||
}
|
||||
},
|
||||
None => "unknown",
|
||||
};
|
||||
write!(f, "Quote [{display_open_state}]")
|
||||
let mut meta = String::default();
|
||||
if self.url.is_some() {
|
||||
meta.push_str("+url ");
|
||||
}
|
||||
if self.citation.is_some() {
|
||||
meta.push_str("+citation ");
|
||||
}
|
||||
|
||||
write!(f, "Quote [{}]", meta.trim())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ If you are familiar with Markdown|https://en.wikipedia.org/wiki/Markdown|, you'l
|
|||
|
||||
## Anchors
|
||||
|
||||
Anchors have the following basic syntax:
|
||||
Anchors are the most important and powerful syntactic element you will work with because they can create connections between nodes when you use them. They have the following basic syntax:
|
||||
|
||||
`
|
||||
anchor|destination
|
||||
|
|
@ -197,11 +197,18 @@ anchor|destination
|
|||
For example:
|
||||
|
||||
`
|
||||
DRC|DemocraticRepublicOfTheCongo
|
||||
particles|ParticlePhysics
|
||||
`
|
||||
|
||||
This example will render as the word "particles" pointing to a node with ID `ParticlePhysics` because the destination is not an external URL.
|
||||
|
||||
An external anchor looks like this:
|
||||
|
||||
`
|
||||
docs|https://en.jutty.dev/node/Documentation
|
||||
`
|
||||
|
||||
As shown above, anchors can point to external addresses. These are identified by the presence of either a `:` or a `/` character in the destination. Otherwise, the anchor will point to a node with an ID matching the destination. This means your anchors to external URLs, special handlers such as `mailto:user@domain.com` and destinations relative to the website root like `/about` will all work as intended without being interpreted as node IDs.
|
||||
External anchors are identified by the presence of either a `:` or a `/` character in the destination. This works for special handlers, such as `mailto:user@domain.com`, and destinations relative to the website root like `/about`.
|
||||
|
||||
If the left side contains spaces, you need a leading `|` character:
|
||||
|
||||
|
|
@ -223,6 +230,8 @@ For internal anchors, most punctuation is automatically separated from the ancho
|
|||
This gem|PreciousStone, though green, was not an emerald.
|
||||
`
|
||||
|
||||
> This gem|PreciousStone, though green, was not an emerald.
|
||||
|
||||
However, for external anchors, you want to add a third `|` to explicitly set the end because external URLs can have all sorts of arbitrary characters.
|
||||
|
||||
### Node anchors
|
||||
|
|
@ -235,14 +244,6 @@ A node ID wrapped in two `|` characters will become an anchor to that node:
|
|||
|ParticlePhysics|
|
||||
`
|
||||
|
||||
And two words separated by a single anchor allow you to set a display text and destination:
|
||||
|
||||
`
|
||||
particles|ParticlePhysics
|
||||
`
|
||||
|
||||
This example will render as "particles|ParticlePhysics": the word particles pointing to a node with id `ParticlePhysics`.
|
||||
|
||||
en can resolve IDs case insensitively (with priority to case-sensitive matches) and will also collapse spaces when trying to resolve an ID, so you can also write:
|
||||
|
||||
`
|
||||
|
|
@ -285,6 +286,9 @@ You can use `[ ]` and `[x]` to render checkboxes:
|
|||
- [x] done
|
||||
`
|
||||
|
||||
- [ ] not done
|
||||
- [x] done
|
||||
|
||||
## Blocks
|
||||
|
||||
A block is any group of lines separated by empty lines:
|
||||
|
|
@ -292,12 +296,12 @@ A block is any group of lines separated by empty lines:
|
|||
`
|
||||
block A
|
||||
still block A
|
||||
still block A
|
||||
block A's last line
|
||||
|
||||
block B starts here
|
||||
block B ends here
|
||||
|
||||
block C starts here
|
||||
still block C
|
||||
this is block C
|
||||
`
|
||||
|
||||
By default, a block not starting with any special syntax is a paragraph, such as this very line you are reading.
|
||||
|
|
@ -310,7 +314,7 @@ b
|
|||
c
|
||||
`
|
||||
|
||||
You still get "a b c" as a result. This is the case for paragraphs and blockquotes, but not for lists, verse blocks and preformatted text.
|
||||
You still get "a b c" as a result. This is the case for paragraphs, but not for lists, verse blocks and preformatted text. Blockquotes support both modes.
|
||||
|
||||
This is useful when editing your text, allowing you to break some thoughts and special syntax without losing control over where your paragraph ends, particularly when handling huge paragraphs.
|
||||
|
||||
|
|
@ -332,7 +336,7 @@ While useful to break a few lines on demand, if you have a large block of lines
|
|||
|
||||
### Verse
|
||||
|
||||
Verse blocks are delimited by a `&` character at their first and last line and are useful to avoid precisely this line-joining behavior of most blocks. They will break all lines without need for a trailing `<` character:
|
||||
Verse blocks are delimited by a `&` character at their first and last line and are useful to avoid precisely this line-joining behavior of paragraphs. They will break all lines without need for a trailing `<` character:
|
||||
|
||||
`
|
||||
&
|
||||
|
|
@ -342,8 +346,6 @@ Verse blocks are delimited by a `&` character at their first and last line and a
|
|||
&
|
||||
`
|
||||
|
||||
This will be rendered as:
|
||||
|
||||
&
|
||||
these lines
|
||||
break just fine
|
||||
|
|
@ -352,37 +354,79 @@ This will be rendered as:
|
|||
|
||||
### Quotes
|
||||
|
||||
A block of lines starting with a `>` character create a quote:
|
||||
A block of lines starting with a `>` character will render as a quote:
|
||||
|
||||
`
|
||||
> this is a quote
|
||||
> Who'll change old lamps for new ones?
|
||||
`
|
||||
|
||||
> Who'll change old lamps for new ones?
|
||||
|
||||
Quote blocks have two forms. If you prepend all blocks with a `>`, line breaks will be preserved, not collapsing the whole quote into a single line:
|
||||
|
||||
> a quote where all lines start with > BR
|
||||
> still inside BR
|
||||
> still inside BR
|
||||
>
|
||||
> above was a line with just a > plus a break BR
|
||||
> still inside BR
|
||||
> next is an empty line BR
|
||||
`
|
||||
> When I was alive
|
||||
> I was dust which was,
|
||||
> But now I am dust in dust
|
||||
> I am dust which never was.
|
||||
`
|
||||
|
||||
> When I was alive
|
||||
> I was dust which was,
|
||||
> But now I am dust in dust
|
||||
> I am dust which never was.
|
||||
|
||||
If you would like the quote to be collapsed into a single line instead, you can leave just the first `>` and continue until the next empty line:
|
||||
|
||||
> this quote starts here
|
||||
and continues here
|
||||
until an empty line is found
|
||||
`
|
||||
> Dois grandes mitos dominam a história oficial do Brasil:
|
||||
o mito da índole pacífica do brasileiro e o da "democracia racial".
|
||||
-- Benedita da Silva, Speech on the Federal Senate, March 3rd, 1995
|
||||
`
|
||||
|
||||
> Dois grandes mitos dominam a história oficial do Brasil:
|
||||
o mito da índole pacífica do brasileiro e o da "democracia racial".
|
||||
-- Benedita da Silva, Speech on the Federal Senate, March 3rd, 1995
|
||||
|
||||
You can still use `<` characters to force line breaks in this case.
|
||||
|
||||
#### Citation
|
||||
|
||||
To add a citation to your qutoe block, you can add lines starting with two `-` characters:
|
||||
To add a citation to your quote block, start a line with two `-` characters:
|
||||
|
||||
> a quote with _nested_ formatting *syntax and in* particular an anchor|Roadmap BR
|
||||
> the quote continuation as it goes on and on
|
||||
-- quote by |Person Johnson|https://personjohnson.com
|
||||
`
|
||||
> with no more communion
|
||||
> to down as morning pick-me-ups
|
||||
> to sweeten afternoon naps
|
||||
> to soothe nightmares
|
||||
-- Assotto Saint, The Language of Dust
|
||||
`
|
||||
|
||||
> with no more communion
|
||||
> to down as morning pick-me-ups
|
||||
> to sweeten afternoon naps
|
||||
> to soothe nightmares
|
||||
-- Assotto Saint, The Language of Dust
|
||||
|
||||
If you have a more complex citation, you can use multiple lines starting with `--`. All such lines will be joined together in the citation. If you need to break lines, use the `<` character at the end of a line:
|
||||
|
||||
`
|
||||
> Dois grandes mitos dominam a história oficial do Brasil:
|
||||
o mito da índole pacífica do brasileiro e o da "democracia racial".
|
||||
-- Benedita da Silva,
|
||||
-- |Speech on the Federal Senate|https://www25.senado.leg.br/web/atividade/pronunciamentos/-/p/pronunciamento/165765|,
|
||||
-- March 3rd, 1995, <
|
||||
-- _Dia Internacional para a Eliminação da Discriminação Racial._
|
||||
`
|
||||
|
||||
> Dois grandes mitos dominam a história oficial do Brasil:
|
||||
o mito da índole pacífica do brasileiro e o da "democracia racial".
|
||||
-- Benedita da Silva,
|
||||
-- |Speech on the Federal Senate|https://www25.senado.leg.br/web/atividade/pronunciamentos/-/p/pronunciamento/165765|,
|
||||
-- March 3rd, 1995, <
|
||||
-- Dia Internacional para a Eliminação da Discriminação Racial.
|
||||
|
||||
The first URL found in your citation will be used as the blockquote element's `cite` field.
|
||||
|
||||
### Lists
|
||||
|
||||
|
|
@ -394,6 +438,10 @@ A block of lines starting with a `-` character will be rendered as an unordered
|
|||
- crimson
|
||||
`
|
||||
|
||||
- cyan
|
||||
- amber
|
||||
- crimson
|
||||
|
||||
Lines starting with a `+` character will create numbered lists instead:
|
||||
|
||||
`
|
||||
|
|
@ -402,6 +450,10 @@ Lines starting with a `+` character will create numbered lists instead:
|
|||
+ san
|
||||
`
|
||||
|
||||
+ ichi
|
||||
+ ni
|
||||
+ san
|
||||
|
||||
## Rendering unformatted text
|
||||
|
||||
The backtick character `\\`` can be used to render unformatted blocks and inline text:
|
||||
|
|
@ -410,6 +462,8 @@ The backtick character `\\`` can be used to render unformatted blocks and inline
|
|||
The asterisk `*` is special in en markup syntax.
|
||||
`
|
||||
|
||||
> The asterisk `*` is special in en markup syntax.
|
||||
|
||||
Using the syntax above, the asterisk won't be interpreted as the start of bold formatting and instead will be shown like this: `*`.
|
||||
|
||||
This is useful for code but also for rendering characters with special meaning you wish to mention literally.
|
||||
|
|
@ -437,8 +491,6 @@ If you need some more advanced feature that is not supported directly by en's ma
|
|||
</table>
|
||||
`
|
||||
|
||||
Which will render to:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td> Hi, </td>
|
||||
|
|
@ -653,8 +705,8 @@ en is only possible thanks to a number of projects and people:
|
|||
- Neovim|https://neovim.io/
|
||||
- foot|https://codeberg.org/dnkl/foot
|
||||
- tmux|https://github.com/tmux/tmux/
|
||||
- |Void Linux|https://voidlinux.org/ and its kernel|https://www.kernel.org/
|
||||
- LibreWolf|https://librewolf.net/
|
||||
- |Debian|https://debian.org/ and its kernel|https://www.kernel.org/
|
||||
- LibreWolf|https://librewolf.net/ and its upstream |Mozilla Firefox|https://www.firefox.com/
|
||||
- InkScape|https://inkscape.org/
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ main {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
p, section li {
|
||||
p:not(.quote-citation), section li {
|
||||
font-family: prose, sans-serif;
|
||||
}
|
||||
|
||||
|
|
@ -337,12 +337,23 @@ blockquote {
|
|||
font-size: calc(var(--base-font-size) * 1.6);
|
||||
}
|
||||
|
||||
.verse {
|
||||
p.verse {
|
||||
font-family: serifed;
|
||||
font-size: calc(var(--base-font-size) * 1.6);
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.quote-citation {
|
||||
margin: 0.5em 0 0 0;
|
||||
text-indent: -1.2em;
|
||||
padding-left: 1em;
|
||||
font-size: calc(var(--base-font-size) * 0.8);
|
||||
}
|
||||
|
||||
.quote-citation::before {
|
||||
content: "— ";
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
* {
|
||||
background: #222222;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
{% endif %}
|
||||
{% if incoming %}
|
||||
<div class="connections-incoming">
|
||||
<h3>Incoming</h4>
|
||||
<h3>Incoming</h3>
|
||||
<ul>
|
||||
{% for connection in incoming %}
|
||||
<li>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue