diff --git a/CHANGELOG.md b/CHANGELOG.md index db28eab..276695e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,10 @@ - All Tera global fns are now rebuilt on changes - Use flags for port/interface in `gutenberg serve` - Fix various issues with headers markdown rendering - +- Rename `insert_anchor` in section front-matter to `insert_anchor_links` +- Remove `insert_anchor_links` from the config: it wasn't used +- Add `class` variable to `gist` shortcode +- Add reading analytics to sections content ## 0.1.3 (2017-08-31) diff --git a/components/config/src/lib.rs b/components/config/src/lib.rs index 4663d24..1e709b6 100644 --- a/components/config/src/lib.rs +++ b/components/config/src/lib.rs @@ -47,10 +47,6 @@ pub struct Config { pub generate_tags_pages: Option, /// Whether to generate categories and individual tag categories if some pages have them. Defaults to true pub generate_categories_pages: Option, - /// Whether to insert a link for each header like in Github READMEs. Defaults to false - /// The default template can be overridden by creating a `anchor-link.html` template and CSS will need to be - /// written if you turn that on. - pub insert_anchor_links: Option, /// Whether to compile the `sass` directory and output the css files into the static folder pub compile_sass: Option, @@ -84,7 +80,6 @@ impl Config { set_default!(config.rss_limit, 20); set_default!(config.generate_tags_pages, false); set_default!(config.generate_categories_pages, false); - set_default!(config.insert_anchor_links, false); set_default!(config.compile_sass, false); set_default!(config.extra, HashMap::new()); @@ -174,7 +169,6 @@ impl Default for Config { rss_limit: Some(10_000), generate_tags_pages: Some(true), generate_categories_pages: Some(true), - insert_anchor_links: Some(false), compile_sass: Some(false), extra: None, build_timestamp: Some(1), diff --git a/components/content/src/section.rs b/components/content/src/section.rs index 54e749f..9de4949 100644 --- a/components/content/src/section.rs +++ b/components/content/src/section.rs @@ -10,6 +10,7 @@ use front_matter::{SectionFrontMatter, split_section_content}; use errors::{Result, ResultExt}; use utils::fs::read_file; use utils::templates::render_template; +use utils::site::get_reading_analytics; use rendering::{Context, Header, markdown_to_html}; use page::Page; @@ -96,7 +97,7 @@ impl Section { config.highlight_theme.clone().unwrap(), &self.permalink, permalinks, - self.meta.insert_anchor.unwrap() + self.meta.insert_anchor_links.unwrap() ); let res = markdown_to_html(&self.raw_content, &context)?; self.content = res.0; @@ -139,7 +140,7 @@ impl Section { impl ser::Serialize for Section { fn serialize(&self, serializer: S) -> StdResult where S: ser::Serializer { - let mut state = serializer.serialize_struct("section", 10)?; + let mut state = serializer.serialize_struct("section", 12)?; state.serialize_field("content", &self.content)?; state.serialize_field("permalink", &self.permalink)?; state.serialize_field("title", &self.meta.title)?; @@ -149,6 +150,9 @@ impl ser::Serialize for Section { state.serialize_field("permalink", &self.permalink)?; state.serialize_field("pages", &self.pages)?; state.serialize_field("subsections", &self.subsections)?; + let (word_count, reading_time) = get_reading_analytics(&self.raw_content); + state.serialize_field("word_count", &word_count)?; + state.serialize_field("reading_time", &reading_time)?; state.serialize_field("toc", &self.toc)?; state.end() } diff --git a/components/content/src/sorting.rs b/components/content/src/sorting.rs index 40c592d..6bb8870 100644 --- a/components/content/src/sorting.rs +++ b/components/content/src/sorting.rs @@ -33,7 +33,7 @@ pub fn sort_pages(pages: Vec, sort_by: SortBy) -> (Vec, Vec) { (can_be_sorted, cannot_be_sorted) } -/// Horribly inefficient way to set previous and next on each pages +/// Horribly inefficient way to set previous and next on each pages that skips drafts /// So many clones pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec { let mut res = Vec::with_capacity(input.len()); diff --git a/components/front_matter/src/lib.rs b/components/front_matter/src/lib.rs index b5d7a75..4340e5d 100644 --- a/components/front_matter/src/lib.rs +++ b/components/front_matter/src/lib.rs @@ -28,9 +28,13 @@ lazy_static! { #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum SortBy { + /// Most recent to oldest Date, + /// Lower order comes last Order, + /// Lower weight comes first Weight, + /// No sorting None, } diff --git a/components/front_matter/src/page.rs b/components/front_matter/src/page.rs index 3985ede..1576531 100644 --- a/components/front_matter/src/page.rs +++ b/components/front_matter/src/page.rs @@ -33,6 +33,7 @@ pub struct PageFrontMatter { /// Integer to use to order content. Highest is at the bottom, lowest first pub weight: Option, /// All aliases for that page. Gutenberg will create HTML templates that will + /// redirect to this #[serde(skip_serializing)] pub aliases: Option>, /// Specify a template different from `page.html` to use for that page diff --git a/components/front_matter/src/section.rs b/components/front_matter/src/section.rs index 6de4c1b..72f8e96 100644 --- a/components/front_matter/src/section.rs +++ b/components/front_matter/src/section.rs @@ -20,8 +20,8 @@ pub struct SectionFrontMatter { /// Whether to sort by "date", "order", "weight" or "none". Defaults to `none`. #[serde(skip_serializing)] pub sort_by: Option, - /// The weight for this section. This is used by the parent section to order its subsections. - /// Higher values means it ends at the end. + /// Used by the parent section to order its subsections. + /// Higher values means it will be at the end. #[serde(skip_serializing)] pub weight: Option, /// Optional template, if we want to specify which template to render for that page @@ -33,10 +33,10 @@ pub struct SectionFrontMatter { /// Path to be used by pagination: the page number will be appended after it. Defaults to `page`. #[serde(skip_serializing)] pub paginate_path: Option, - /// Whether to insert a link for each header like in Github READMEs. Defaults to false - /// The default template can be overridden by creating a `anchor-link.html` template and CSS will need to be - /// written if you turn that on. - pub insert_anchor: Option, + /// Whether to insert a link for each header like the ones you can see in this site if you hover one + /// The default template can be overridden by creating a `anchor-link.html` in the `templates` directory + /// This can also be set in a section front-matter if you only want it for + pub insert_anchor_links: Option, /// Whether to render that section or not. Defaults to `true`. /// Useful when the section is only there to organize things but is not meant /// to be used directly, like a posts section in a personal site @@ -70,8 +70,8 @@ impl SectionFrontMatter { f.sort_by = Some(SortBy::None); } - if f.insert_anchor.is_none() { - f.insert_anchor = Some(InsertAnchor::None); + if f.insert_anchor_links.is_none() { + f.insert_anchor_links = Some(InsertAnchor::None); } if f.weight.is_none() { @@ -111,7 +111,7 @@ impl Default for SectionFrontMatter { paginate_path: Some(DEFAULT_PAGINATE_PATH.to_string()), render: Some(true), redirect_to: None, - insert_anchor: Some(InsertAnchor::None), + insert_anchor_links: Some(InsertAnchor::None), extra: None, } } diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index 14b57e2..f87c4ec 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -177,7 +177,8 @@ impl Site { .map(|entry| { let path = entry.as_path(); Section::from_file(path, config) - }).collect::>() + }) + .collect::>() }; let pages = { @@ -189,7 +190,8 @@ impl Site { .map(|entry| { let path = entry.as_path(); Page::from_file(path, config) - }).collect::>() + }) + .collect::>() }; // Kinda duplicated code for add_section/add_page but necessary to do it that @@ -224,8 +226,7 @@ impl Site { let config = &self.config; self.pages.par_iter_mut() - .map(|(_, page)| page) - .map(|page| { + .map(|(_, page)| { let insert_anchor = pages_insert_anchors[&page.file.path]; page.render_markdown(permalinks, tera, config, insert_anchor) }) @@ -233,8 +234,7 @@ impl Site { .reduce(|| Ok(()), Result::and)?; self.sections.par_iter_mut() - .map(|(_, section)| section) - .map(|section| section.render_markdown(permalinks, tera, config)) + .map(|(_, section)| section.render_markdown(permalinks, tera, config)) .fold(|| Ok(()), Result::and) .reduce(|| Ok(()), Result::and)?; } @@ -295,7 +295,7 @@ impl Site { /// Defaults to `AnchorInsert::None` if no parent section found pub fn find_parent_section_insert_anchor(&self, parent_path: &PathBuf) -> InsertAnchor { match self.sections.get(&parent_path.join("_index.md")) { - Some(s) => s.meta.insert_anchor.unwrap(), + Some(s) => s.meta.insert_anchor_links.unwrap(), None => InsertAnchor::None } } diff --git a/components/site/test_site/content/posts/_index.md b/components/site/test_site/content/posts/_index.md index 37a519c..cbb5609 100644 --- a/components/site/test_site/content/posts/_index.md +++ b/components/site/test_site/content/posts/_index.md @@ -2,5 +2,5 @@ title = "Posts" paginate_by = 2 template = "section_paginated.html" -insert_anchor = "left" +insert_anchor_links = "left" +++ diff --git a/components/site/tests/site.rs b/components/site/tests/site.rs index cf7cfe2..a545a63 100644 --- a/components/site/tests/site.rs +++ b/components/site/tests/site.rs @@ -1,4 +1,5 @@ extern crate site; +extern crate front_matter; extern crate tempdir; use std::env; @@ -8,6 +9,7 @@ use std::io::prelude::*; use tempdir::TempDir; use site::Site; +use front_matter::InsertAnchor; #[test] @@ -296,6 +298,7 @@ fn can_build_site_and_insert_anchor_links() { path.push("test_site"); let mut site = Site::new(&path, "config.toml").unwrap(); site.load().unwrap(); + let tmp_dir = TempDir::new("example").expect("create temp dir"); let public = &tmp_dir.path().join("public"); site.set_output_path(&public); diff --git a/components/templates/src/builtins/shortcodes/gist.html b/components/templates/src/builtins/shortcodes/gist.html index 89d0fbf..44f051f 100644 --- a/components/templates/src/builtins/shortcodes/gist.html +++ b/components/templates/src/builtins/shortcodes/gist.html @@ -1,3 +1,3 @@ -
+
diff --git a/docs/config.toml b/docs/config.toml index 39a54ff..73d5349 100644 --- a/docs/config.toml +++ b/docs/config.toml @@ -1,9 +1,10 @@ base_url = "https://example.com" -title = "Gutenberg - your one-stop static site engine" +title = "Gutenberg" description = "Everything you need to make a static site engine in one binary. And it's fast" compile_sass = true highlight_code = true +insert_anchor_links = true [extra] author = "Vincent Prouillet" diff --git a/docs/content/documentation/_index.md b/docs/content/documentation/_index.md index 9326600..a1b9104 100644 --- a/docs/content/documentation/_index.md +++ b/docs/content/documentation/_index.md @@ -10,24 +10,18 @@ Getting started Content - Organisation - - Pages - Sections + - Pages - Shortcodes - - Internal links + - Internal links & deep linking - Table of contents - - Deep linking (# links) - Syntax highlighting - - Pagination - - Tag & categories - - RSS - - Sitemap Templates - Intro - Each kind of page and the variables available - Built-in global functions - Built-in filters - - Debugging Theme - Installing & customising a theme diff --git a/docs/content/documentation/content/_index.md b/docs/content/documentation/content/_index.md new file mode 100644 index 0000000..ddb65dc --- /dev/null +++ b/docs/content/documentation/content/_index.md @@ -0,0 +1,7 @@ ++++ +title = "Content" +weight = 2 +sort_by = "weight" +redirect_to = "documentation/content/overview" +insert_anchor_links = "left" ++++ diff --git a/docs/content/documentation/content/linking.md b/docs/content/documentation/content/linking.md new file mode 100644 index 0000000..afc7881 --- /dev/null +++ b/docs/content/documentation/content/linking.md @@ -0,0 +1,37 @@ ++++ +title = "Internal links & deep linking" +weight = 50 ++++ + +## Header id and anchor insertion +While rendering the markdown content, a unique id will automatically be assigned to each header. This id is created +by converting the header text to a [slug](https://en.wikipedia.org/wiki/Semantic_URL#Slug), appending numbers at the end +if the slug already exists for that article. For example: + +```md +# Something exciting! <- something-exciting +## Example code <- example-code + +# Something else <- something-else +## Example code <- example-code-1 +``` + +## Anchor insertion +It is possible to have Gutenberg automatically insert anchor links next to the header, as you can see on the site you are currently +reading if you hover a title. + +This option is set at the section level, look up the `insert_anchor_links` variable on the +[Section front-matter page](./documentation/content/section.md#front-matter). + +The default template is very basic and will need CSS tweaks in your project to look decent. +If you want to change the anchor template, itt can easily be overwritten by +creating a `anchor-link.html` file in the `templates` directory. + +## Internal links +Linking to other pages and their headers is so common that Gutenberg adds a +special syntax to Markdown links to handle them: start the link with `./` and point to the `.md` file you want +to link to. The path to the file starts from the `content` directory. + +For example, linking to a file located at `content/pages/about.md` would be `[my link](./pages/about.md)`. +You can still link to a header directly: `[my link](./pages/about.md#example)` would work as expected, granted +the `example` id exists on the header. diff --git a/docs/content/documentation/content/overview.md b/docs/content/documentation/content/overview.md new file mode 100644 index 0000000..90a6e0a --- /dev/null +++ b/docs/content/documentation/content/overview.md @@ -0,0 +1,48 @@ ++++ +title = "Overview" +weight = 10 ++++ + + +Gutenberg uses the folder structure to determine the site structure. +Each folder in the `content` directory represents a [section](./documentation/content/section.md) +that contains [pages](./documentation/content/page.md) : your `.md` files. + +```bash +. +└── content + ├── content + │   └── something.md // -> https://mywebsite.com/content/something/ + ├── blog + │   ├── cli-usage.md // -> https://mywebsite.com/blog/cli-usage/ + │   ├── configuration.md // -> https://mywebsite.com/blog/configuration/ + │   ├── directory-structure.md // -> https://mywebsite.com/blog/directory-structure/ + │   ├── _index.md // -> https://mywebsite.com/blog/ + │   └── installation.md // -> https://mywebsite.com/blog/installation/ + └── landing + └── _index.md // -> https://mywebsite.com/landing/ +``` + +Obviously, each page path (the part after the `base_url`, for example `blog/`) can be customised by setting the wanted value +in the [page front-matter](./documentation/content/page.md#front-matter). + +You might have noticed a file named `_index.md` in the example above. +This file will be used for the metadata and content of the section itself and is not considered a page. + +To make sure the terminology used in the rest of the documentation is understood, let's go over the example above. + +The `content` directory in this case has three `sections`: `content`, `blog` and `landing`. The `content` section has only +one page, `something.md`, the `landing` section has no page and the `blog` section has 4 pages: `cli-usage.md`, `configuration.md`, `directory-structure.md` +and `installation.md`. + +While not shown in the example, sections can be nested indefinitely. + +The `content` directory is not limited to markup files though: it's natural to want to co-locate a page and some related +assets. Gutenberg supports that pattern out of the box: create a folder, add a `index.md` file and as many non-markdown as you want. +Those assets will be copied in the same folder when building so you can just use a relative path to access them. + +```bash +└── with-assets + ├── index.md + └── yavascript.js +``` diff --git a/docs/content/documentation/content/page.md b/docs/content/documentation/content/page.md new file mode 100644 index 0000000..177087b --- /dev/null +++ b/docs/content/documentation/content/page.md @@ -0,0 +1,71 @@ ++++ +title = "Page" +weight = 30 ++++ + +A page is any file ending with `.md` in the `content` directory, except files +named `_index/md`. + +## Front-matter + +The front-matter is a set of metadata embedded in a file. In Gutenberg, +it is at the beginning of the file, surrounded by `+++` and uses TOML. + +None of the front-matter variables are mandatory. However the opening and closing `+++` are required even if there are +no variables in it. + +Here is an example page with all the variables available: + +```md ++++ +title = "" +description = "" + +# The date of the post. +# 2 formats are allowed: YYYY-MM-DD (2012-10-02) and RFC3339 (2002-10-02T15:00:00Z) +date = "" + +# A draft page will not be present in prev/next pagination +draft = false + +# If filled, it will use that slug instead of the filename to make up the URL +# It will still use the section path though +slug = "" + +# The URL the content will appear at +# If set, it cannot be an empty string and will override both `slug` and the filename +# and the sections' path won't be used +url = "" + +# An array of strings allowing you to group pages with them +tags = [] + +# An overarching category name for that page, allowing you to group pages with it +category = "" + +# The order as defined in the Section page +order = 0 + +# The weight as defined in the Section page +weight = 0 + +# Use aliases if you are moving content but want to redirect previous URLs to the +# current one. This takes an array of path, not URLs. +aliases = [] + +# Template to use to render this page +template = "page.html" + +# Your own data +[extra] ++++ + +Some content +``` + +## Summary +You can ask Gutenberg to create a summary if you only want to show the first +paragraph of each page in a list for example. +To do so, add `` in your content at the point where you want the +summary to end and the content up to that point will be also available separately +in the template. diff --git a/docs/content/documentation/content/section.md b/docs/content/documentation/content/section.md new file mode 100644 index 0000000..c789c44 --- /dev/null +++ b/docs/content/documentation/content/section.md @@ -0,0 +1,89 @@ ++++ +title = "Section" +weight = 20 ++++ + +A section is automatically created implicitly when a folder is found +in the `content` section. + +You can add `_index.md` file to a folder to augment a section and give it +some metadata and/or content. + +## Front-matter + +The front-matter is a set of metadata embedded in a file. In Gutenberg, +it is at the beginning of the file, surrounded by `+++` and uses TOML. + +As the file itself is optional, none of the front-matter variables are +mandatory. However the opening and closing `+++` are required even if there are +no variables in it. + +Here is an example `_index.md` with all the variables available: + + +```md ++++ +title = "" + +description = "" + +# Whether to sort by "date", "order", "weight" or "none". More on that below +sort_by = "none" + +# Used by the parent section to order its subsections. +# Higher values means it will be at the end. +weight = 0 + +# Template to use to render this section page +template = "section.html" + +# How many pages to be displayed per paginated page. +# No pagination will happen if this isn't set or if the value is 0 +paginate_by = 0 + +# If set, will be the path used by paginated page and the page number will be appended after it. +# For example the default would be page/1 +paginate_by = "page" + +# Whether to insert a link for each header like the ones you can see in this site if you hover one +# The default template can be overridden by creating a `anchor-link.html` in the `templates` directory +# This can also be set in a section front-matter if you only want it for +# Options are "left", "right" and "none" +insert_anchor_links = "none" + +# Whether to render that section or not. +# Useful when the section is only there to organize things but is not meant +# to be used directly +render = true + +# Whether to redirect when landing on that section. Defaults to `None`. +# Useful for the same reason as `render` but when you don't want a 404 when +# landing on the root section page +redirect_to = "" + +# Your own data +[extra] ++++ + +Some content +``` + +Keep in mind that the variables only apply to the direct pages, not to the subsections' pages. This means +you can only paginate the pages directly in the section folder for example. + +## Sorting +Sections' pages can be sorted three different ways, not counting the unsorted default. +Sorting is enabled by setting the `sort_by` front-matter variable. + +Any page that cannot be sorted, for example if missing the date variable while sorting by `date`, will be ignored and +won't be rendered. The terminal will warn you if this is happening. + +### `date` +This will sort all pages by their `date` field, from the most recent to the oldest. + +### `weight` +This will be sort all pages by their `weight` field. Heavier weights fall at the bottom: 5 would be before 10. + +### `order` +This will be sort all pages by their `order` field. Order is the opposite of weight, think of it as enumerating +the content: this is my first post, my second, etc. A page with `order: 5` will appear after a page with `order: 10` in the sorted list. diff --git a/docs/content/documentation/content/shortcodes.md b/docs/content/documentation/content/shortcodes.md new file mode 100644 index 0000000..c1363c2 --- /dev/null +++ b/docs/content/documentation/content/shortcodes.md @@ -0,0 +1,166 @@ ++++ +title = "Shortcodes" +weight = 40 ++++ + +While Markdown is good at writing, it isn't great when you need write inline +HTML to add some styling for example. + +To solve this, Gutenberg borrows the concept of [shortcodes](https://codex.wordpress.org/Shortcode_API) +from WordPress. +In our case, the shortcode corresponds to a template that is defined in the `templates/shortcodes` directory or a built-in one. + +## Writing a shortcode +Let's write a shortcode to embed YouTube videos as an example. +In a file called `youtube.html` in the `templates/shortcodes` directory, paste the +following: + +```jinja2 +
+ +
+``` + +This template is very straightforward: an iframe pointing to the YouTube embed URL wrapped in a `
`. +In terms of input, it expects at least one variable: `id`. Since the other variables +are in a `if` statement, we can assume they are optional. + +That's it, Gutenberg will now recognise this template as a shortcode named `youtube` (the filename minus the `.html` extension). + +## Using shortcodes + +There are two kinds of shortcodes: ones that do no take a body like the YouTube example above and ones that do, a quote for example. +In both cases, their arguments must be named and they will all be passed to the template. + +Do note that shortcodes in code blocks will be ignored. + +### Shortcodes without body +Those look like rendering a variable in Tera. + +On a new line, call the shortcode as if it was a function in a variable block. All the examples below are valid +calls of the YouTube shortcode. + +```md +{{ youtube(id="w7Ft2ymGmfc") }} + +{{ youtube(id="w7Ft2ymGmfc", autoplay=true) }} + +{{ youtube(id="w7Ft2ymGmfc", autoplay=true, class="youtube") }} +``` + +### Shortcodes with body +Those look like a block in Tera. +For example, let's imagine we have the following shortcode `quote.html` template: + +```jinja2 +
+ {{ body }}
+ -- {{ author}} +
+``` + +We could use it in our markup file like so: + +```md +{% quote(author="Vincent") %} +A quote +{% end %} +``` + +The `body` variable used in the shortcode template will be implicitly passed down to the rendering +context automatically. + +## Built-in shortcodes + +Gutenberg comes with a few built-in shortcodes. If you want to override a default shortcode template, +simply place a `{shortcode_name}.html` file in the `templates/shortcodes` directory and Gutenberg will +use that instead. + +### YouTube +Embed a responsive player for a YouTube video. + +The arguments are: + +- `id`: the video id (mandatory) +- `class`: a class to add the `div` surrounding the iframe +- `autoplay`: whether to autoplay the video on load + +Usage example: + +```md +{{ youtube(id="w7Ft2ymGmfc") }} + +{{ youtube(id="w7Ft2ymGmfc", autoplay=true) }} + +{{ youtube(id="w7Ft2ymGmfc", autoplay=true, class="youtube") }} +``` + +Result example: + +{{ youtube(id="w7Ft2ymGmfc") }} + +### Vimeo +Embed a player for a Vimeo video. + +The arguments are: + +- `id`: the video id (mandatory) +- `class`: a class to add the `div` surrounding the iframe + +Usage example: + +```md +{{ vimeo(id="124313553") }} + +{{ vimeo(id="124313553", class="vimeo") }} +``` + +Result example: + +{{ vimeo(id="124313553") }} + +### Streamable +Embed a player for a Streamable video. + +The arguments are: + +- `id`: the video id (mandatory) +- `class`: a class to add the `div` surrounding the iframe + +Usage example: + +```md +{{ streamable(id="2zt0") }} + +{{ streamable(id="2zt0", class="streamble") }} +``` + +Result example: + +{{ streamable(id="2zt0") }} + +### Gist +Embed a [Github gist](). + +The arguments are: + +- `url`: the url to the gist (mandatory) +- `file`: by default, the shortcode will pull every file from the URL unless a specific filename is requested +- `class`: a class to add the `div` surrounding the iframe + +Usage example: + +```md +{{ gist(id="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57") }} + +{{ gist(id="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57", class="gist") }} +``` + +Result example: + +{{ gist(url="https://gist.github.com/Keats/e5fb6aad409f28721c0ba14161644c57") }} diff --git a/docs/content/documentation/content/syntax-highlighting.md b/docs/content/documentation/content/syntax-highlighting.md new file mode 100644 index 0000000..2ef0e6b --- /dev/null +++ b/docs/content/documentation/content/syntax-highlighting.md @@ -0,0 +1,111 @@ ++++ +title = "Syntax Highlighting" +weight = 80 ++++ + +Gutenberg comes with built-in syntax highlighting but you first +need to enable it in the [configuration](./documentation/getting-started/configuration.md). + +Once this is done, Gutenberg will automatically highlight all code blocks +in your content. A code block in Markdown looks like the following: + +````md + +```rust +let highlight = true; +``` + +```` + +You can replace the `rust` by the language you want to highlight. +Here is a full list of the supported languages and the short name you can use: + +``` +- Plain Text -> ["txt"] +- Assembly x86 (NASM) -> ["asm", "inc", "nasm"] +- Elm -> ["elm"] +- Handlebars -> ["handlebars", "handlebars.html", "hbr", "hbrs", "hbs", "hdbs", "hjs", "mu", "mustache", "rac", "stache", "template", "tmpl"] +- Jinja2 -> ["j2", "jinja2"] +- Julia -> ["jl"] +- LESS -> ["less"] +- ASP -> ["asa"] +- HTML (ASP) -> ["asp"] +- ActionScript -> ["as"] +- AppleScript -> ["applescript", "script editor"] +- Batch File -> ["bat", "cmd"] +- NAnt Build File -> ["build"] +- C# -> ["cs", "csx"] +- C++ -> ["cpp", "cc", "cp", "cxx", "c++", "C", "h", "hh", "hpp", "hxx", "h++", "inl", "ipp"] +- C -> ["c", "h"] +- CSS -> ["css", "css.erb", "css.liquid"] +- Clojure -> ["clj"] +- D -> ["d", "di"] +- Diff -> ["diff", "patch"] +- Erlang -> ["erl", "hrl", "Emakefile", "emakefile"] +- HTML (Erlang) -> ["yaws"] +- Go -> ["go"] +- Graphviz (DOT) -> ["dot", "DOT"] +- Groovy -> ["groovy", "gvy", "gradle"] +- HTML -> ["html", "htm", "shtml", "xhtml", "inc", "tmpl", "tpl"] +- Haskell -> ["hs"] +- Literate Haskell -> ["lhs"] +- Java Server Page (JSP) -> ["jsp"] +- Java -> ["java", "bsh"] +- JavaDoc -> [] +- Java Properties -> ["properties"] +- JSON -> ["json", "sublime-settings", "sublime-menu", "sublime-keymap", "sublime-mousemap", "sublime-theme", "sublime-build", "sublime-project", "sublime-completions", "sublime-commands", "sublime-macro"] +- JavaScript -> ["js", "htc"] +- Regular Expressions (Javascript) -> [] +- BibTeX -> ["bib"] +- LaTeX Log -> [] +- LaTeX -> ["tex", "ltx"] +- TeX -> ["sty", "cls"] +- Lisp -> ["lisp", "cl", "l", "mud", "el", "scm", "ss", "lsp", "fasl"] +- Lua -> ["lua"] +- Make Output -> [] +- Makefile -> ["make", "GNUmakefile", "makefile", "Makefile", "OCamlMakefile", "mak", "mk"] +- Markdown -> ["md", "mdown", "markdown", "markdn"] +- MultiMarkdown -> [] +- MATLAB -> ["matlab"] +- OCaml -> ["ml", "mli"] +- OCamllex -> ["mll"] +- OCamlyacc -> ["mly"] +- camlp4 -> [] +- Objective-C++ -> ["mm", "M", "h"] +- Objective-C -> ["m", "h"] +- PHP Source -> [] +- PHP -> ["php", "php3", "php4", "php5", "php7", "phps", "phpt", "phtml"] +- Pascal -> ["pas", "p", "dpr"] +- Perl -> ["pl", "pm", "pod", "t", "PL"] +- Python -> ["py", "py3", "pyw", "pyi", "rpy", "cpy", "SConstruct", "Sconstruct", "sconstruct", "SConscript", "gyp", "gypi", "Snakefile", "wscript"] +- Regular Expressions (Python) -> [] +- R Console -> [] +- R -> ["R", "r", "s", "S", "Rprofile"] +- Rd (R Documentation) -> ["rd"] +- HTML (Rails) -> ["rails", "rhtml", "erb", "html.erb"] +- JavaScript (Rails) -> ["js.erb"] +- Ruby Haml -> ["haml", "sass"] +- Ruby on Rails -> ["rxml", "builder"] +- SQL (Rails) -> ["erbsql", "sql.erb"] +- Regular Expression -> ["re"] +- reStructuredText -> ["rst", "rest"] +- Ruby -> ["rb", "Appfile", "Appraisals", "Berksfile", "Brewfile", "capfile", "cgi", "Cheffile", "config.ru", "Deliverfile", "Fastfile", "fcgi", "Gemfile", "gemspec", "Guardfile", "irbrc", "jbuilder", "podspec", "prawn", "rabl", "rake", "Rakefile", "Rantfile", "rbx", "rjs", "ruby.rail", "Scanfile", "simplecov", "Snapfile", "thor", "Thorfile", "Vagrantfile"] +- Cargo Build Results -> [] +- Rust -> ["rs"] +- SQL -> ["sql", "ddl", "dml"] +- Scala -> ["scala", "sbt"] +- Shell Script (Bash) -> ["sh", "bash", "zsh", ".bash_aliases", ".bash_functions", ".bash_login", ".bash_logout", ".bash_profile", ".bash_variables", ".bashrc", ".profile", ".textmate_init"] +- HTML (Tcl) -> ["adp"] +- Tcl -> ["tcl"] +- Textile -> ["textile"] +- XML -> ["xml", "xsd", "xslt", "tld", "dtml", "rss", "opml", "svg"] +- YAML -> ["yaml", "yml", "sublime-syntax"] +- Generic Config -> ["cfg", "conf", "config", "ini", "pro"] +- Linker Script -> ["ld"] +- TOML -> ["toml", "tml"] +- TypeScript -> ["ts"] +- TypeScriptReact -> ["tsx"] +- VimL -> ["vim"] +``` + +If you want to highlight a language not on that list, please open an issue or a pull request on the [Gutenberg repo](https://github.com/Keats/gutenberg). diff --git a/docs/content/documentation/content/table-of-contents.md b/docs/content/documentation/content/table-of-contents.md new file mode 100644 index 0000000..4a8a241 --- /dev/null +++ b/docs/content/documentation/content/table-of-contents.md @@ -0,0 +1,33 @@ ++++ +title = "Table of Contents" +weight = 60 ++++ + +Each page/section will automatically generate a table of content for itself based on the headers present. + +TODO: add link for template variables +It is available in the template through `section.toc` and `page.toc`. You can view the [template variables]() +documentation for information on its structure. + +Here is an example of using that field to render a 2-level table of content: + +```jinja2 +
    +{% for h1 in page.toc %} +
  • + {{ h1.title }} + {% if h1.children %} + + {% endif %} +
  • +{% endfor %} +
+``` + +While headers are neatly ordered in that example, it will work just as well with disjoint headers. diff --git a/docs/content/documentation/getting-started/_index.md b/docs/content/documentation/getting-started/_index.md index 7d61261..ff983e0 100644 --- a/docs/content/documentation/getting-started/_index.md +++ b/docs/content/documentation/getting-started/_index.md @@ -1,4 +1,7 @@ +++ title = "Getting Started" -sort_by = "order" +weight = 1 +sort_by = "weight" +redirect_to = "documentation/getting-started/installation" +insert_anchor_links = "left" +++ diff --git a/docs/content/documentation/getting-started/cli-usage.md b/docs/content/documentation/getting-started/cli-usage.md index 3d1ca14..fc13493 100644 --- a/docs/content/documentation/getting-started/cli-usage.md +++ b/docs/content/documentation/getting-started/cli-usage.md @@ -1,6 +1,43 @@ +++ title = "CLI usage" -order = 2 +weight = 2 +++ -Hey +Gutenberg only has 3 commands: init, build and serve. + +You can view the help of the whole program by running `gutenberg --help` and +the command help by running `gutenberg --help`. + +## init + +Creates the directory structure used by Gutenberg at the given directory. + +```bash +$ gutenberg init +``` + +will create a new folder named `my_site` and the files/folders needed by +Gutenberg. + +## build + +This will build the whole site in the `public` directory. + +```bash +$ gutenberg build +``` + +## serve + +This will build and serve the site using a local server. You can also specify +the interface/port combination to use if you want something different than the default (`127.0.0.1:1111`). + +```bash +$ gutenberg serve +$ gutenberg serve --port 2000 +$ gutenberg serve --interface 0.0.0.0 +$ gutenberg serve --interface 0.0.0.0 --port 2000 +``` + +The serve command will watch all your content and will provide live reload, without +hard refresh if possible. diff --git a/docs/content/documentation/getting-started/configuration.md b/docs/content/documentation/getting-started/configuration.md index 8285a2c..a3a2717 100644 --- a/docs/content/documentation/getting-started/configuration.md +++ b/docs/content/documentation/getting-started/configuration.md @@ -1,6 +1,71 @@ +++ title = "Configuration" -order = 4 +weight = 4 +++ -Hey +The default configuration will be enough to get Gutenberg running locally but not more than that. +It follows the philosophy of only paying for what you need: almost everything is turned off by default. + +To change the config, edit the `config.toml` file. +If you are not familiar with TOML, have a look at [the TOML Spec](https://github.com/toml-lang/toml) +to learn about it. + +Only one variable - `base_url` - is mandatory, everything else is optional. You can find all variables +used by Gutenberg config as well as their default values below: + + +```toml +# Base URL of the site, the only required config argument +base_url = "mywebsite.com" + +# Used in RSS by default +title = "" +description = "" +language_code = "en" + +# Theme name to use +theme = "" + +# Highlight all code blocks found +highlight_code = false + +# Which theme to use for the code highlighting. See below for list of accepted values +highlight_theme = "base16-ocean-dark" + +# Whether to generate a RSS feed automatically +generate_rss = false + +# The number of articles to include in the RSS feed +rss_limit = 20 + +# Whether to generate a tags page and individual tag pages for pages with tags +generate_tags_pages = false + +# Whether to generate a categories page and individual category pages for pages with a category +generate_categories_pages = false + +# Whether to compile the Sass files found in the `sass` directory +compile_sass = false + +# You can put any kind of data in there and it will be accessible in all templates +[extra] +``` + +## Syntax highlighting + +Gutenberg currently has the following highlight themes available: + +- base16-ocean-dark +- base16-ocean-light +- gruvbox-dark +- gruvbox-light +- inspired-github +- kronuz +- material-dark +- material-light +- monokai +- solarized-dark +- solarized-light + +Gutenberg uses the Sublime Text themes, making it very easy to add more. +If you want a theme not on that list, please open an issue or a pull request on the [Gutenberg repo](https://github.com/Keats/gutenberg). diff --git a/docs/content/documentation/getting-started/directory-structure.md b/docs/content/documentation/getting-started/directory-structure.md index b08854f..7efd630 100644 --- a/docs/content/documentation/getting-started/directory-structure.md +++ b/docs/content/documentation/getting-started/directory-structure.md @@ -1,6 +1,47 @@ +++ title = "Directory structure" -order = 3 +weight = 3 +++ -Hey +After running `gutenberg init`, you should see the following structure in your folder: + + +```bash +. +├── config.toml +├── content +├── sass +├── static +├── templates +└── themes + +5 directories, 1 file +``` + +Here's a high level overview of each of these folders and `config.toml`. + +## `config.toml` +A mandatory configuration file of Gutenberg in TOML format. +It is explained in details in the [Configuration page](./documentation/getting-started/configuration.md). + +## `content` +Where all your markup content lies: this will most likely be mostly `.md` files. +Each folder in the `content` directory represents a [section](./documentation/content/section.md) +that contains [pages](./documentation/content/page.md) : your `.md` files. + +To learn more, read [the content overview](./documentation/content/overview.md). + +## `sass` +Contains the [Sass](http://sass-lang.com) files to be compiled. Non-Sass files will be ignored. + +## `static` +Contains any kind of files. All the files/folders in the `static` folder will be copied as-is in the output directory. + +## `templates` +Contains all the [Tera](tera.netlify.com) templates that will be used to render this site. +Have a look at the [Templates](./documentation/templates/_index.md) to learn more on the default templates +and the variables available. + +## `themes` +Contains themes that can be used for that site. If you are not planning to use themes, you can safely ignore +this folder and let it be. If you want to learn about themes, head to the [themes documentation](./documentation/themes/_index.md). diff --git a/docs/content/documentation/getting-started/installation.md b/docs/content/documentation/getting-started/installation.md index cd4c351..16605f5 100644 --- a/docs/content/documentation/getting-started/installation.md +++ b/docs/content/documentation/getting-started/installation.md @@ -1,6 +1,31 @@ +++ title = "Installation" -order = 1 +weight = 1 +++ -Hey +Gutenberg provides pre-built binaries for Mac OS, Linux and Windows on the +[Github release page](https://github.com/Keats/gutenberg/releases). + +## Using brew on Mac OS + +TODO: it's not on brew right now + +## Windows + +TODO: i have no clue whatsoever about packages in Windows + +## Archlinux + +TODO: add a `gutenberg-bin` in AUR and explain how to install it + +## From source +To build it from source, you will need to have Git, [Rust and Cargo](https://www.rust-lang.org/en-US/) +installed. + +From a terminal, you can now run the following command: + +```bash +$ cargo build --release +``` + +The binary will be available in the `target/release` folder. diff --git a/docs/content/documentation/templates/_index.md b/docs/content/documentation/templates/_index.md new file mode 100644 index 0000000..cbb5929 --- /dev/null +++ b/docs/content/documentation/templates/_index.md @@ -0,0 +1,6 @@ ++++ +title = "Templates" +weight = 3 +sort_by = "weight" +insert_anchor_links = "left" ++++ diff --git a/docs/content/documentation/templates/overview.md b/docs/content/documentation/templates/overview.md new file mode 100644 index 0000000..90f1b24 --- /dev/null +++ b/docs/content/documentation/templates/overview.md @@ -0,0 +1,73 @@ ++++ +title = "Overview" +weight = 10 ++++ + +Gutenberg uses the [Tera](tera.netlify.com) template engine. +This documentation will only touch how templates work in Gutenberg, please read +the [Tera template documentation](https://tera.netlify.com/docs/templates/) if you want +to know how write them. If you are familiar with Jinja2, Liquid or Twig, this should be +a breeze. + +All templates live in the `templates` directory and built-in or themes templates can +be overriden by creating a template with same name in the correct path. For example, +you can override the RSS template by creating a `templates/rss.xml` file. + +If you are not sure what is available in a template, you can just stick `{{ __tera_context }}` in it +to print the whole context. + +A few variables are available on all templates except for RSS/Sitemap: + +- `config`: the [configuration](./documentation/getting-started/configuration.md) without any modifications +- `current_path`: the path (full URL without the `base_url`) of the current page +- `current_url`: the full URL for that page + +## Built-in filters +Gutenberg adds a few filters, in addition of the ones already present in Tera. + +### markdown +Converts the given variable to HTML using Markdown. This doesn't apply any of the +features that Gutenberg adds to Markdown: internal links, shortcodes etc won't work. + +### base64_encode +Encode the variable to base64. + +### base64_decode +Decode the variable from base64. + + +## Built-in global functions +Gutenberg adds a few global functions to Tera in order to make it easier to develop complex sites. + +### `get_page` +Takes a path to a `.md` file and returns the associated page + +```jinja2 +{% set page = get_page(path="blog/page2.md") %} +``` + +### `get_section` +Takes a path to a `_index.md` file and returns the associated section + +```jinja2 +{% set section = get_page(path="blog/_index.md") %} +``` + +###` get_url` +Gets the permalink for the given path. +If the path starts with `./`, it will be understood as an internal +link like the ones used in markdown. + +```jinja2 +{% set url = get_url(path="./blog/_index.md") %} +``` + +This can also be used to get the permalinks for static assets for example if +we want to link to the file that is located at `static/css/app.css`: + +```jinja2 +{{ get_url(path="css/app.css") }} +``` + +In the case of non-internal links, you can also add a cachebust of the format `?t=1290192` at the end of a URL +by passing `cachebust=true` to the `get_url` function. diff --git a/docs/content/documentation/templates/pages-sections.md b/docs/content/documentation/templates/pages-sections.md new file mode 100644 index 0000000..54a2b17 --- /dev/null +++ b/docs/content/documentation/templates/pages-sections.md @@ -0,0 +1,88 @@ ++++ +title = "Index, Sections and Pages" +weight = 20 ++++ + +First off, it is important to know that in Gutenberg the index +page is actually a section like any other: you can add metadata +and content by adding `_index.md` at the root of the `content` folder. + +Pages and sections are actually very similar. + +## Page variables +By default, Gutenberg will try to load `templates/page.html`. If there isn't +one, it will render the built-in template: a blank page. + +Whichever template you decide to render, you will get a `page` variable in your template +with the following fields: + + +```ts +content: String; +title: String?; +description: String?; +date: String?; +slug: String; +path: String; +permalink: String; +summary: String?; +tags: Array; +category: String?; +extra: HashMap; +// Naive word count, will not work for languages without whitespace +word_count: Number; +// Based on https://help.medium.com/hc/en-us/articles/214991667-Read-time +reading_time: Number; +// `previous` and `next` are only filled if the content can be sorted +previous: Page?; +next: Page?; +// See the Table of contents section below for more details +toc: Array
; +``` + +## Section variables +By default, Gutenberg will try to load `templates/section.html`. If there isn't +one, it will render the built-in template: a blank page. + +Whichever template you decide to render, you will get a `section` variable in your template +with the following fields: + + +```ts +content: String; +title: String?; +description: String?; +date: String?; +slug: String; +path: String; +permalink: String; +extra: HashMap; +// Pages directly in this section, sorted if asked +pages: Array; +// Direct subsections to this section, sorted by subsections weight +subsections: Array
; +// Naive word count, will not work for languages without whitespace +word_count: Number; +// Based on https://help.medium.com/hc/en-us/articles/214991667-Read-time +reading_time: Number; +// See the Table of contents section below for more details +toc: Array
; +``` + +## Table of contents + +Both page and section have a `toc` field which corresponds to an array of `Header`. +A `Header` has the following fields: + +```ts +// the hx level +level: 1 | 2 | 3 | 4 | 5 | 6; +// the generated slug id +id: String; +// the text of the header +title: String; +// a link pointing directly to the header, using the inserted anchor +permalink: String; +// all lower level headers below this header +children: Array
; +``` diff --git a/docs/content/documentation/themes/_index.md b/docs/content/documentation/themes/_index.md new file mode 100644 index 0000000..938a8e2 --- /dev/null +++ b/docs/content/documentation/themes/_index.md @@ -0,0 +1,5 @@ ++++ +title = "Themes" +weight = 4 +sort_by = "weight" ++++ diff --git a/docs/sass/_docs.scss b/docs/sass/_docs.scss index 12edefe..5405e2a 100644 --- a/docs/sass/_docs.scss +++ b/docs/sass/_docs.scss @@ -1,3 +1,44 @@ .documentation { padding: 3rem; + display: flex; + + &__sidebar { + margin-right: 2rem; + + ul { + padding-left: 0; + list-style: none; + + ul { + padding-left: 1rem; + + li.active a { + color: red; + } + } + } + } + + &__content { + flex: 1; + } + + a { + color: $background; + padding-bottom: 2px; + border-bottom: 1px solid $background; + + &:hover { + text-decoration: none; + } + + &:visited { + color: $background; + } + } + + iframe { + width: 100%; + min-height: 400px; + } } diff --git a/docs/templates/documentation.html b/docs/templates/documentation.html index 6ce8855..3734a8f 100644 --- a/docs/templates/documentation.html +++ b/docs/templates/documentation.html @@ -8,19 +8,24 @@ {% set section = get_section(path="documentation/_index.md") %}
- hey + {% block doc_content %} + {% endblock doc_content %}
{% endblock content %} diff --git a/docs/templates/index.html b/docs/templates/index.html index 017c6ee..5ddf93f 100644 --- a/docs/templates/index.html +++ b/docs/templates/index.html @@ -37,7 +37,7 @@

Everything built-in

Gutenberg comes with Sass compilation, syntax highlighting and - a other features that usually require using additional tools + other features that usually require using additional tools or use JavaScript libraries on your site.

diff --git a/docs/templates/page.html b/docs/templates/page.html new file mode 100644 index 0000000..10d1a98 --- /dev/null +++ b/docs/templates/page.html @@ -0,0 +1,7 @@ +{% extends "documentation.html" %} + +{% block title %}{{ super() }} - {{ page.title }} {% endblock title %} +{% block doc_content %} +

{{page.title}}

+ {{page.content | safe}} +{% endblock doc_content %} diff --git a/src/rebuild.rs b/src/rebuild.rs index e2c9391..5c1b16e 100644 --- a/src/rebuild.rs +++ b/src/rebuild.rs @@ -60,7 +60,7 @@ fn find_section_front_matter_changes(current: &SectionFrontMatter, other: &Secti if current.paginate_by != other.paginate_by || current.paginate_path != other.paginate_path - || current.insert_anchor != other.insert_anchor { + || current.insert_anchor_links != other.insert_anchor_links { changes_needed.push(SectionChangesNeeded::RenderWithPages); // Nothing else we can do return changes_needed; @@ -177,7 +177,6 @@ pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> { let page = Page::from_file(path, &site.config)?; match site.add_page(page, true)? { Some(prev) => { - site.register_tera_global_fns(); // Updating a page let current = site.pages[path].clone(); // Front matter didn't change, only content did @@ -212,6 +211,7 @@ pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> { }, }; } + site.register_tera_global_fns(); return Ok(()); },