commit
a3a5b9ea06
98
.gitmodules
vendored
98
.gitmodules
vendored
|
@ -1,39 +1,63 @@
|
|||
[submodule "sublime_syntaxes/Packages"]
|
||||
path = sublime_syntaxes/Packages
|
||||
[submodule "sublime/syntaxes/Packages"]
|
||||
path = sublime/syntaxes/Packages
|
||||
url = https://github.com/sublimehq/Packages.git
|
||||
[submodule "sublime_syntaxes/sublimeassembly"]
|
||||
path = sublime_syntaxes/sublimeassembly
|
||||
url = https://github.com/Nessphoro/sublimeassembly.git
|
||||
[submodule "sublime_syntaxes/LESS-sublime"]
|
||||
path = sublime_syntaxes/LESS-sublime
|
||||
url = https://github.com/danro/LESS-sublime.git
|
||||
[submodule "sublime_syntaxes/Handlebars"]
|
||||
path = sublime_syntaxes/Handlebars
|
||||
url = https://github.com/daaain/Handlebars.git
|
||||
[submodule "sublime_syntaxes/Julia-sublime"]
|
||||
path = sublime_syntaxes/Julia-sublime
|
||||
url = https://github.com/JuliaEditorSupport/Julia-sublime.git
|
||||
[submodule "sublime_syntaxes/sublime_toml_highlighting"]
|
||||
path = sublime_syntaxes/sublime_toml_highlighting
|
||||
url = https://github.com/Jayflux/sublime_toml_highlighting.git
|
||||
[submodule "sublime_syntaxes/SublimeTextLinkerSyntax"]
|
||||
path = sublime_syntaxes/SublimeTextLinkerSyntax
|
||||
url = https://github.com/jbw3/SublimeTextLinkerSyntax.git
|
||||
[submodule "sublime_syntaxes/Sublime-GenericConfig"]
|
||||
path = sublime_syntaxes/Sublime-GenericConfig
|
||||
url = https://github.com/skozlovf/Sublime-GenericConfig.git
|
||||
[submodule "sublime_syntaxes/Sublime-VimL"]
|
||||
path = sublime_syntaxes/Sublime-VimL
|
||||
[submodule "sublime/syntaxes/awk-sublime"]
|
||||
path = sublime/syntaxes/awk-sublime
|
||||
url = https://github.com/JohnNilsson/awk-sublime.git
|
||||
[submodule "sublime/syntaxes/AsciiDoc"]
|
||||
path = sublime/syntaxes/AsciiDoc
|
||||
url = https://github.com/SublimeText/AsciiDoc.git
|
||||
[submodule "sublime/syntaxes/Sublime-CMakeLists"]
|
||||
path = sublime/syntaxes/Sublime-CMakeLists
|
||||
url = https://github.com/zyxar/Sublime-CMakeLists.git
|
||||
[submodule "sublime/syntaxes/SublimeTextLinkerSyntax"]
|
||||
path = sublime/syntaxes/SublimeTextLinkerSyntax
|
||||
url = https://github.com/jbw3/SublimeTextLinkerSyntax
|
||||
[submodule "sublime/syntaxes/Docker.tmbundle"]
|
||||
path = sublime/syntaxes/Docker.tmbundle
|
||||
url = https://github.com/asbjornenge/Docker.tmbundle.git
|
||||
[submodule "sublime/syntaxes/Sublime-VimL"]
|
||||
path = sublime/syntaxes/Sublime-VimL
|
||||
url = https://github.com/SalGnt/Sublime-VimL.git
|
||||
[submodule "sublime_syntaxes/TypeScript-TmLanguage"]
|
||||
path = sublime_syntaxes/TypeScript-TmLanguage
|
||||
url = https://github.com/Microsoft/TypeScript-TmLanguage
|
||||
[submodule "sublime_syntaxes/SublimeElmLanguageSupport"]
|
||||
path = sublime_syntaxes/SublimeElmLanguageSupport
|
||||
url = https://github.com/elm-community/SublimeElmLanguageSupport
|
||||
[submodule "sublime_syntaxes/Sublime-CMakeLists"]
|
||||
path = sublime_syntaxes/Sublime-CMakeLists
|
||||
url = https://github.com/zyxar/Sublime-CMakeLists
|
||||
[submodule "sublime_syntaxes/Swift-for-f-ing-sublime"]
|
||||
path = sublime_syntaxes/Swift-for-f-ing-sublime
|
||||
url = https://github.com/colinta/Swift-for-f-ing-sublime.git
|
||||
[submodule "sublime/syntaxes/elixir-sublime-syntax"]
|
||||
path = sublime/syntaxes/elixir-sublime-syntax
|
||||
url = https://github.com/princemaple/elixir-sublime-syntax.git
|
||||
[submodule "sublime/syntaxes/SublimeElmLanguageSupport"]
|
||||
path = sublime/syntaxes/SublimeElmLanguageSupport
|
||||
url = https://github.com/elm-community/SublimeElmLanguageSupport.git
|
||||
[submodule "sublime/syntaxes/sublimetext-fsharp"]
|
||||
path = sublime/syntaxes/sublimetext-fsharp
|
||||
url = https://github.com/hoest/sublimetext-fsharp.git
|
||||
[submodule "sublime/syntaxes/sublime-fish"]
|
||||
path = sublime/syntaxes/sublime-fish
|
||||
url = https://github.com/Phidica/sublime-fish.git
|
||||
[submodule "sublime/syntaxes/SublimeFortran"]
|
||||
path = sublime/syntaxes/SublimeFortran
|
||||
url = https://github.com/315234/SublimeFortran.git
|
||||
[submodule "sublime/syntaxes/GraphQL-SublimeText3"]
|
||||
path = sublime/syntaxes/GraphQL-SublimeText3
|
||||
url = https://github.com/dncrews/GraphQL-SublimeText3.git
|
||||
[submodule "sublime/syntaxes/Sublime-GenericConfig"]
|
||||
path = sublime/syntaxes/Sublime-GenericConfig
|
||||
url = https://github.com/skozlovf/Sublime-GenericConfig.git
|
||||
[submodule "sublime/syntaxes/sublime-jinja2"]
|
||||
path = sublime/syntaxes/sublime-jinja2
|
||||
url = https://github.com/Martin819/sublime-jinja2.git
|
||||
[submodule "sublime/syntaxes/Julia-sublime"]
|
||||
path = sublime/syntaxes/Julia-sublime
|
||||
url = https://github.com/JuliaEditorSupport/Julia-sublime.git
|
||||
[submodule "sublime/syntaxes/LESS-sublime"]
|
||||
path = sublime/syntaxes/LESS-sublime
|
||||
url = https://github.com/danro/LESS-sublime.git
|
||||
[submodule "sublime/syntaxes/sublime-purescript-syntax"]
|
||||
path = sublime/syntaxes/sublime-purescript-syntax
|
||||
url = https://github.com/tellnobody1/sublime-purescript-syntax.git
|
||||
[submodule "sublime/syntaxes/SublimeSass"]
|
||||
path = sublime/syntaxes/SublimeSass
|
||||
url = https://github.com/braver/SublimeSass.git
|
||||
[submodule "sublime/syntaxes/sublime_toml_highlighting"]
|
||||
path = sublime/syntaxes/sublime_toml_highlighting
|
||||
url = https://github.com/jasonwilliams/sublime_toml_highlighting.git
|
||||
[submodule "sublime/syntaxes/vue-syntax-highlight"]
|
||||
path = sublime/syntaxes/vue-syntax-highlight
|
||||
url = https://github.com/vuejs/vue-syntax-highlight.git
|
||||
|
|
26
CHANGELOG.md
26
CHANGELOG.md
|
@ -1,5 +1,29 @@
|
|||
# Changelog
|
||||
|
||||
## 0.11.0 (2020-05-25)
|
||||
|
||||
### Breaking
|
||||
- RSS feed support has been altered to allow, *and default to*, Atom feeds, Atom being technically superior and just as widely-supported in normal use cases.
|
||||
- New config value `feed_filename`, defaulting to `atom.xml` (change to `rss.xml` to reinstate the old behaviour)
|
||||
- Config value `rss_limit` is renamed to `feed_limit`
|
||||
- Config value `languages.*.rss` is renamed to `languages.*.feed`
|
||||
- Config value `generate_rss` is renamed to `generate_feed`
|
||||
|
||||
Users with existing feeds should either set `feed_filename = "rss.xml"` in config.toml to keep things the same, or set up a 3xx redirect from rss.xml to atom.xml so that existing feed consumers aren’t broken.
|
||||
|
||||
- The feed template variable `last_build_date` is renamed to `last_updated` to more accurately reflect its semantics
|
||||
- The sitemap template’s `SitemapEntry` type’s `date` field has been renamed to `updated` to reflect that it will use the `updated` front-matter field if available, rather than `date`
|
||||
|
||||
### Other
|
||||
- Add `updated` front-matter field for pages, which sitemap templates will use for the `SitemapEntry.date` field instead of the `date` front-matter field, and which the default Atom feed template will use
|
||||
- Add `lang` to the feed template context
|
||||
- Add `taxonomy` and `term` to the feed template context for taxonomy feeds
|
||||
- Fix link checker not looking for anchor with capital id/name
|
||||
- Pass missing `lang` template parameter to taxonomy list template
|
||||
- Fix default index section not having its path set to '/'
|
||||
- Change cachebust strategy to use SHA256 instead of timestamp
|
||||
- Fix
|
||||
|
||||
## 0.10.1 (2020-03-12)
|
||||
|
||||
- Set user agent for HTTP requests
|
||||
|
@ -12,8 +36,6 @@
|
|||
### Breaking
|
||||
- Remove `toc` variable in section/page context and pass it to `page.toc` and `section.toc` instead so they are
|
||||
accessible everywhere
|
||||
- [Slugification](https://en.wikipedia.org/wiki/Slug_(web_publishing)#Slug) of paths, taxonomies and anchors is now configurable. By default, everything will still be slugified like in previous versions.
|
||||
See documentation for information on how to disable it.
|
||||
|
||||
### Other
|
||||
- Add zenburn syntax highlighting theme
|
||||
|
|
|
@ -35,7 +35,7 @@ Tools > Developer > New Syntax from ... and put it at the root of `sublime_synta
|
|||
You can also add a submodule to the repository of the wanted syntax:
|
||||
|
||||
```bash
|
||||
$ cd sublime_syntaxes
|
||||
$ cd sublime/syntaxes
|
||||
$ git submodule add https://github.com/elm-community/SublimeElmLanguageSupport
|
||||
```
|
||||
|
||||
|
@ -51,7 +51,7 @@ $ git submodule update --remote --merge
|
|||
And finally from the root of the components/config crate run the following command:
|
||||
|
||||
```bash
|
||||
$ cargo run --example generate_sublime synpack ../../sublime_syntaxes ../../sublime_syntaxes/newlines.packdump
|
||||
$ cargo run --example generate_sublime synpack ../../sublime/syntaxes ../../sublime/syntaxes/newlines.packdump
|
||||
```
|
||||
|
||||
### Adding a theme
|
||||
|
@ -60,7 +60,7 @@ More themes can be easily added to Zola, just make a PR with the wanted theme ad
|
|||
and run the following command from the root of the components/config:
|
||||
|
||||
```bash
|
||||
$ cargo run --example generate_sublime themepack ../../sublime_themes ../../sublime_themes/all.themedump
|
||||
$ cargo run --example generate_sublime themepack ../../sublime/themes ../../sublime/themes/all.themedump
|
||||
```
|
||||
|
||||
You should see the list of themes being added.
|
||||
|
|
2825
Cargo.lock
generated
2825
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "zola"
|
||||
version = "0.10.1"
|
||||
version = "0.11.0"
|
||||
authors = ["Vincent Prouillet <hello@vincentprouillet.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
|
|
|
@ -19,9 +19,9 @@ stages:
|
|||
linux-stable:
|
||||
imageName: 'ubuntu-16.04'
|
||||
rustup_toolchain: stable
|
||||
linux-1.39:
|
||||
linux-pinned:
|
||||
imageName: 'ubuntu-16.04'
|
||||
rustup_toolchain: 1.39.0
|
||||
rustup_toolchain: 1.41.0
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
steps:
|
||||
|
|
|
@ -20,8 +20,8 @@ Register-ArgumentCompleter -Native -CommandName 'zola' -ScriptBlock {
|
|||
|
||||
$completions = @(switch ($command) {
|
||||
'zola' {
|
||||
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Path to a config file other than config.toml')
|
||||
[CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'Path to a config file other than config.toml')
|
||||
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Path to a config file other than config.toml in the root of project')
|
||||
[CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'Path to a config file other than config.toml in the root of project')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Prints help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Prints version information')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
complete -c zola -n "__fish_use_subcommand" -s c -l config -d 'Path to a config file other than config.toml'
|
||||
complete -c zola -n "__fish_use_subcommand" -s c -l config -d 'Path to a config file other than config.toml in the root of project'
|
||||
complete -c zola -n "__fish_use_subcommand" -s h -l help -d 'Prints help information'
|
||||
complete -c zola -n "__fish_use_subcommand" -s V -l version -d 'Prints version information'
|
||||
complete -c zola -n "__fish_use_subcommand" -f -a "init" -d 'Create a new Zola project'
|
||||
|
|
|
@ -11,7 +11,7 @@ serde_derive = "1"
|
|||
chrono = "0.4"
|
||||
globset = "0.4"
|
||||
lazy_static = "1"
|
||||
syntect = "=3.2.0"
|
||||
syntect = "4.1"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
utils = { path = "../utils" }
|
||||
|
|
|
@ -26,7 +26,10 @@ fn main() {
|
|||
(Some(ref cmd), Some(ref package_dir), Some(ref packpath_newlines)) if cmd == "synpack" => {
|
||||
let mut builder = SyntaxSetBuilder::new();
|
||||
builder.add_plain_text_syntax();
|
||||
builder.add_from_folder(package_dir, true).unwrap();
|
||||
match builder.add_from_folder(package_dir, true) {
|
||||
Ok(_) => (),
|
||||
Err(e) => println!("Loading error: {:?}", e)
|
||||
};
|
||||
let ss = builder.build();
|
||||
dump_to_file(&ss, packpath_newlines).unwrap();
|
||||
let mut syntaxes: HashMap<String, HashSet<String>> = HashMap::new();
|
||||
|
|
|
@ -47,15 +47,15 @@ impl Default for Slugify {
|
|||
pub struct Language {
|
||||
/// The language code
|
||||
pub code: String,
|
||||
/// Whether to generate a RSS feed for that language, defaults to `false`
|
||||
pub rss: bool,
|
||||
/// Whether to generate a feed for that language, defaults to `false`
|
||||
pub feed: bool,
|
||||
/// Whether to generate search index for that language, defaults to `false`
|
||||
pub search: bool,
|
||||
}
|
||||
|
||||
impl Default for Language {
|
||||
fn default() -> Self {
|
||||
Language { code: String::new(), rss: false, search: false }
|
||||
Language { code: String::new(), feed: false, search: false }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,8 @@ pub struct Taxonomy {
|
|||
/// by this much
|
||||
pub paginate_by: Option<usize>,
|
||||
pub paginate_path: Option<String>,
|
||||
/// Whether to generate a RSS feed only for each taxonomy term, defaults to false
|
||||
pub rss: bool,
|
||||
/// Whether to generate a feed only for each taxonomy term, defaults to false
|
||||
pub feed: bool,
|
||||
/// The language for that taxonomy, only used in multilingual sites.
|
||||
/// Defaults to the config `default_language` if not set
|
||||
pub lang: String,
|
||||
|
@ -99,7 +99,7 @@ impl Default for Taxonomy {
|
|||
name: String::new(),
|
||||
paginate_by: None,
|
||||
paginate_path: None,
|
||||
rss: false,
|
||||
feed: false,
|
||||
lang: String::new(),
|
||||
}
|
||||
}
|
||||
|
@ -155,10 +155,13 @@ pub struct Config {
|
|||
/// Defaults to "base16-ocean-dark"
|
||||
pub highlight_theme: String,
|
||||
|
||||
/// Whether to generate RSS. Defaults to false
|
||||
pub generate_rss: bool,
|
||||
/// The number of articles to include in the RSS feed. Defaults to including all items.
|
||||
pub rss_limit: Option<usize>,
|
||||
/// Whether to generate a feed. Defaults to false.
|
||||
pub generate_feed: bool,
|
||||
/// The number of articles to include in the feed. Defaults to including all items.
|
||||
pub feed_limit: Option<usize>,
|
||||
/// The filename to use for feeds. Used to find the template, too.
|
||||
/// Defaults to "atom.xml", with "rss.xml" also having a template provided out of the box.
|
||||
pub feed_filename: String,
|
||||
/// If set, files from static/ will be hardlinked instead of copied to the output dir.
|
||||
pub hard_link_static: bool,
|
||||
|
||||
|
@ -215,6 +218,10 @@ impl Config {
|
|||
bail!("Highlight theme {} not available", config.highlight_theme)
|
||||
}
|
||||
|
||||
if config.languages.iter().any(|l| l.code == config.default_language) {
|
||||
bail!("Default language `{}` should not appear both in `config.default_language` and `config.languages`", config.default_language)
|
||||
}
|
||||
|
||||
config.build_timestamp = Some(Utc::now().timestamp());
|
||||
|
||||
if !config.ignored_content.is_empty() {
|
||||
|
@ -272,11 +279,12 @@ impl Config {
|
|||
|
||||
/// Makes a url, taking into account that the base url might have a trailing slash
|
||||
pub fn make_permalink(&self, path: &str) -> String {
|
||||
let trailing_bit = if path.ends_with('/') || path.ends_with("rss.xml") || path.is_empty() {
|
||||
""
|
||||
} else {
|
||||
"/"
|
||||
};
|
||||
let trailing_bit =
|
||||
if path.ends_with('/') || path.ends_with(&self.feed_filename) || path.is_empty() {
|
||||
""
|
||||
} else {
|
||||
"/"
|
||||
};
|
||||
|
||||
// Index section with a base url that has a trailing slash
|
||||
if self.base_url.ends_with('/') && path == "/" {
|
||||
|
@ -380,8 +388,9 @@ impl Default for Config {
|
|||
highlight_theme: "base16-ocean-dark".to_string(),
|
||||
default_language: "en".to_string(),
|
||||
languages: Vec::new(),
|
||||
generate_rss: false,
|
||||
rss_limit: None,
|
||||
generate_feed: false,
|
||||
feed_limit: None,
|
||||
feed_filename: "atom.xml".to_string(),
|
||||
hard_link_static: false,
|
||||
taxonomies: Vec::new(),
|
||||
compile_sass: false,
|
||||
|
@ -489,10 +498,10 @@ hello = "world"
|
|||
|
||||
// https://github.com/Keats/gutenberg/issues/486
|
||||
#[test]
|
||||
fn doesnt_add_trailing_slash_to_rss() {
|
||||
fn doesnt_add_trailing_slash_to_feed() {
|
||||
let mut config = Config::default();
|
||||
config.base_url = "http://vincent.is/".to_string();
|
||||
assert_eq!(config.make_permalink("rss.xml"), "http://vincent.is/rss.xml");
|
||||
assert_eq!(config.make_permalink("atom.xml"), "http://vincent.is/atom.xml");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -657,4 +666,19 @@ anchors = "off"
|
|||
assert_eq!(config.slugify.taxonomies, SlugifyStrategy::Safe);
|
||||
assert_eq!(config.slugify.anchors, SlugifyStrategy::Off);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_on_language_set_twice() {
|
||||
let config_str = r#"
|
||||
base_url = "https://remplace-par-ton-url.fr"
|
||||
default_language = "fr"
|
||||
languages = [
|
||||
{ code = "fr" },
|
||||
{ code = "en" },
|
||||
]
|
||||
"#;
|
||||
let config = Config::parse(config_str);
|
||||
let err = config.unwrap_err();
|
||||
assert_eq!("Default language `fr` should not appear both in `config.default_language` and `config.languages`", format!("{}", err));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ use crate::config::Config;
|
|||
lazy_static! {
|
||||
pub static ref SYNTAX_SET: SyntaxSet = {
|
||||
let ss: SyntaxSet =
|
||||
from_binary(include_bytes!("../../../sublime_syntaxes/newlines.packdump"));
|
||||
from_binary(include_bytes!("../../../sublime/syntaxes/newlines.packdump"));
|
||||
ss
|
||||
};
|
||||
pub static ref THEME_SET: ThemeSet =
|
||||
from_binary(include_bytes!("../../../sublime_themes/all.themedump"));
|
||||
from_binary(include_bytes!("../../../sublime/themes/all.themedump"));
|
||||
}
|
||||
|
||||
/// Returns the highlighter and whether it was found in the extra or not
|
||||
|
|
|
@ -7,11 +7,11 @@ use std::path::Path;
|
|||
|
||||
/// Get and parse the config.
|
||||
/// If it doesn't succeed, exit
|
||||
pub fn get_config(path: &Path, filename: &str) -> Config {
|
||||
match Config::from_file(path.join(filename)) {
|
||||
pub fn get_config(filename: &Path) -> Config {
|
||||
match Config::from_file(filename) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
println!("Failed to load {}", filename);
|
||||
println!("Failed to load {}", filename.display());
|
||||
println!("Error: {}", e);
|
||||
::std::process::exit(1);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ impl Theme {
|
|||
let content = read_file_with_error(
|
||||
path,
|
||||
"No `theme.toml` file found. \
|
||||
Is the `theme` defined in your `config.toml present in the `themes` directory \
|
||||
Is the `theme` defined in your `config.toml` present in the `themes` directory \
|
||||
and does it have a `theme.toml` inside?",
|
||||
)?;
|
||||
Theme::parse(&content)
|
||||
|
|
|
@ -8,4 +8,4 @@ edition = "2018"
|
|||
tera = "1"
|
||||
toml = "0.5"
|
||||
image = "0.23"
|
||||
syntect = "=3.2.0"
|
||||
syntect = "4.1"
|
||||
|
|
0
components/errors/src/lib.rs
Executable file → Normal file
0
components/errors/src/lib.rs
Executable file → Normal file
|
@ -16,6 +16,9 @@ pub struct PageFrontMatter {
|
|||
pub title: Option<String>,
|
||||
/// Description in <meta> that appears when linked, e.g. on twitter
|
||||
pub description: Option<String>,
|
||||
/// Updated date
|
||||
#[serde(default, deserialize_with = "from_toml_datetime")]
|
||||
pub updated: Option<String>,
|
||||
/// Date if we want to order pages (ie blog post)
|
||||
#[serde(default, deserialize_with = "from_toml_datetime")]
|
||||
pub date: Option<String>,
|
||||
|
@ -117,6 +120,7 @@ impl Default for PageFrontMatter {
|
|||
PageFrontMatter {
|
||||
title: None,
|
||||
description: None,
|
||||
updated: None,
|
||||
date: None,
|
||||
datetime: None,
|
||||
datetime_tuple: None,
|
||||
|
|
|
@ -195,7 +195,7 @@ mod tests {
|
|||
#[test]
|
||||
fn can_find_valid_language_in_page() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let mut file = FileInfo::new_page(
|
||||
&Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"),
|
||||
&PathBuf::new(),
|
||||
|
@ -208,7 +208,7 @@ mod tests {
|
|||
#[test]
|
||||
fn can_find_valid_language_in_page_with_assets() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let mut file = FileInfo::new_page(
|
||||
&Path::new("/home/vincent/code/site/content/posts/tutorials/python/index.fr.md"),
|
||||
&PathBuf::new(),
|
||||
|
@ -234,7 +234,7 @@ mod tests {
|
|||
#[test]
|
||||
fn errors_on_unknown_language_in_page_with_i18n_on() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("it"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("it"), feed: false, search: false });
|
||||
let mut file = FileInfo::new_page(
|
||||
&Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"),
|
||||
&PathBuf::new(),
|
||||
|
@ -246,7 +246,7 @@ mod tests {
|
|||
#[test]
|
||||
fn can_find_valid_language_in_section() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let mut file = FileInfo::new_section(
|
||||
&Path::new("/home/vincent/code/site/content/posts/tutorials/_index.fr.md"),
|
||||
&PathBuf::new(),
|
||||
|
@ -273,7 +273,7 @@ mod tests {
|
|||
#[test]
|
||||
fn correct_canonical_after_find_language() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let mut file = FileInfo::new_page(
|
||||
&Path::new("/home/vincent/code/site/content/posts/tutorials/python/index.fr.md"),
|
||||
&PathBuf::new(),
|
||||
|
|
|
@ -770,7 +770,7 @@ Hello world
|
|||
#[test]
|
||||
fn can_specify_language_in_filename() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let content = r#"
|
||||
+++
|
||||
+++
|
||||
|
@ -787,7 +787,7 @@ Bonjour le monde"#
|
|||
#[test]
|
||||
fn can_specify_language_in_filename_with_date() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let content = r#"
|
||||
+++
|
||||
+++
|
||||
|
@ -806,7 +806,7 @@ Bonjour le monde"#
|
|||
#[test]
|
||||
fn i18n_frontmatter_path_overrides_default_permalink() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let content = r#"
|
||||
+++
|
||||
path = "bonjour"
|
||||
|
|
|
@ -350,7 +350,7 @@ mod tests {
|
|||
#[test]
|
||||
fn can_specify_language_in_filename() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let content = r#"
|
||||
+++
|
||||
+++
|
||||
|
@ -372,7 +372,7 @@ Bonjour le monde"#
|
|||
#[test]
|
||||
fn can_make_links_to_translated_sections_without_double_trailing_slash() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let content = r#"
|
||||
+++
|
||||
+++
|
||||
|
@ -389,7 +389,7 @@ Bonjour le monde"#
|
|||
#[test]
|
||||
fn can_make_links_to_translated_subsections_with_trailing_slash() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { code: String::from("fr"), rss: false, search: false });
|
||||
config.languages.push(Language { code: String::from("fr"), feed: false, search: false });
|
||||
let content = r#"
|
||||
+++
|
||||
+++
|
||||
|
|
|
@ -63,6 +63,7 @@ pub struct SerializingPage<'a> {
|
|||
ancestors: Vec<String>,
|
||||
title: &'a Option<String>,
|
||||
description: &'a Option<String>,
|
||||
updated: &'a Option<String>,
|
||||
date: &'a Option<String>,
|
||||
year: Option<i32>,
|
||||
month: Option<u32>,
|
||||
|
@ -126,6 +127,7 @@ impl<'a> SerializingPage<'a> {
|
|||
title: &page.meta.title,
|
||||
description: &page.meta.description,
|
||||
extra: &page.meta.extra,
|
||||
updated: &page.meta.updated,
|
||||
date: &page.meta.date,
|
||||
year,
|
||||
month,
|
||||
|
@ -182,6 +184,7 @@ impl<'a> SerializingPage<'a> {
|
|||
title: &page.meta.title,
|
||||
description: &page.meta.description,
|
||||
extra: &page.meta.extra,
|
||||
updated: &page.meta.updated,
|
||||
date: &page.meta.date,
|
||||
year,
|
||||
month,
|
||||
|
|
|
@ -6,7 +6,7 @@ use slotmap::DefaultKey;
|
|||
|
||||
use crate::content::Page;
|
||||
|
||||
/// Used by the RSS feed
|
||||
/// Used by the feed
|
||||
/// There to not have to import sorting stuff in the site crate
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn sort_actual_pages_by_date(a: &&Page, b: &&Page) -> Ordering {
|
||||
|
|
|
@ -169,6 +169,7 @@ impl Taxonomy {
|
|||
let terms: Vec<SerializedTaxonomyItem> =
|
||||
self.items.iter().map(|i| SerializedTaxonomyItem::from_item(i, library)).collect();
|
||||
context.insert("terms", &terms);
|
||||
context.insert("lang", &self.kind.lang);
|
||||
context.insert("taxonomy", &self.kind);
|
||||
context.insert("current_url", &config.make_permalink(&self.kind.name));
|
||||
context.insert("current_path", &self.kind.name);
|
||||
|
@ -457,7 +458,7 @@ mod tests {
|
|||
#[test]
|
||||
fn can_make_taxonomies_in_multiple_languages() {
|
||||
let mut config = Config::default();
|
||||
config.languages.push(Language { rss: false, code: "fr".to_string(), search: false });
|
||||
config.languages.push(Language { feed: false, code: "fr".to_string(), search: false });
|
||||
let mut library = Library::new(2, 0, true);
|
||||
|
||||
config.taxonomies = vec![
|
||||
|
@ -568,7 +569,7 @@ mod tests {
|
|||
let mut config = Config::default();
|
||||
config.slugify.taxonomies = SlugifyStrategy::Safe;
|
||||
config.languages.push(Language {
|
||||
rss: false,
|
||||
feed: false,
|
||||
code: "fr".to_string(),
|
||||
..Language::default()
|
||||
});
|
||||
|
@ -601,7 +602,7 @@ mod tests {
|
|||
let mut config = Config::default();
|
||||
config.slugify.taxonomies = SlugifyStrategy::On;
|
||||
config.languages.push(Language {
|
||||
rss: false,
|
||||
feed: false,
|
||||
code: "fr".to_string(),
|
||||
..Language::default()
|
||||
});
|
||||
|
|
|
@ -5,11 +5,15 @@ authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.10", features = ["blocking", "rustls-tls"] }
|
||||
lazy_static = "1"
|
||||
|
||||
config = { path = "../config" }
|
||||
errors = { path = "../errors" }
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["blocking", "rustls-tls"]
|
||||
|
||||
[dev-dependencies]
|
||||
mockito = "0.23"
|
||||
mockito = "0.25"
|
||||
|
|
|
@ -3,50 +3,33 @@ use reqwest::header::{HeaderMap, ACCEPT};
|
|||
use reqwest::{blocking::Client, StatusCode};
|
||||
|
||||
use config::LinkChecker;
|
||||
use errors::Result;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::result;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LinkResult {
|
||||
pub code: Option<StatusCode>,
|
||||
/// Whether the HTTP request didn't make it to getting a HTTP code
|
||||
pub error: Option<String>,
|
||||
pub type Result = result::Result<StatusCode, String>;
|
||||
|
||||
pub fn is_valid(res: &Result) -> bool {
|
||||
match res {
|
||||
Ok(ref code) => code.is_success() || *code == StatusCode::NOT_MODIFIED,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl LinkResult {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
if self.error.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(c) = self.code {
|
||||
return c.is_success() || c == StatusCode::NOT_MODIFIED;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn message(&self) -> String {
|
||||
if let Some(ref e) = self.error {
|
||||
return e.clone();
|
||||
}
|
||||
|
||||
if let Some(c) = self.code {
|
||||
return format!("{}", c);
|
||||
}
|
||||
|
||||
"Unknown error".to_string()
|
||||
pub fn message(res: &Result) -> String {
|
||||
match res {
|
||||
Ok(ref code) => format!("{}", code),
|
||||
Err(ref error) => error.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// Keep history of link checks so a rebuild doesn't have to check again
|
||||
static ref LINKS: Arc<RwLock<HashMap<String, LinkResult>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||
static ref LINKS: Arc<RwLock<HashMap<String, Result>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||
}
|
||||
|
||||
pub fn check_url(url: &str, config: &LinkChecker) -> LinkResult {
|
||||
pub fn check_url(url: &str, config: &LinkChecker) -> Result {
|
||||
{
|
||||
let guard = LINKS.read().unwrap();
|
||||
if let Some(res) = guard.get(url) {
|
||||
|
@ -75,13 +58,13 @@ pub fn check_url(url: &str, config: &LinkChecker) -> LinkResult {
|
|||
};
|
||||
|
||||
match check_page_for_anchor(url, body) {
|
||||
Ok(_) => LinkResult { code: Some(response.status()), error: None },
|
||||
Err(e) => LinkResult { code: None, error: Some(e.to_string()) },
|
||||
Ok(_) => Ok(response.status()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
Ok(response) => {
|
||||
if response.status().is_success() || response.status() == StatusCode::NOT_MODIFIED {
|
||||
LinkResult { code: Some(response.status()), error: None }
|
||||
Ok(response.status())
|
||||
} else {
|
||||
let error_string = if response.status().is_informational() {
|
||||
format!("Informational status code ({}) received", response.status())
|
||||
|
@ -95,10 +78,10 @@ pub fn check_url(url: &str, config: &LinkChecker) -> LinkResult {
|
|||
format!("Non-success status code ({}) received", response.status())
|
||||
};
|
||||
|
||||
LinkResult { code: None, error: Some(error_string) }
|
||||
Err(error_string)
|
||||
}
|
||||
}
|
||||
Err(e) => LinkResult { code: None, error: Some(e.to_string()) },
|
||||
Err(e) => Err(e.to_string()),
|
||||
};
|
||||
|
||||
LINKS.write().unwrap().insert(url.to_string(), res.clone());
|
||||
|
@ -115,14 +98,18 @@ fn has_anchor(url: &str) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_page_for_anchor(url: &str, body: String) -> Result<()> {
|
||||
fn check_page_for_anchor(url: &str, body: String) -> errors::Result<()> {
|
||||
let index = url.find('#').unwrap();
|
||||
let anchor = url.get(index + 1..).unwrap();
|
||||
let checks: [String; 4] = [
|
||||
let checks: [String; 8] = [
|
||||
format!(" id='{}'", anchor),
|
||||
format!(" ID='{}'", anchor),
|
||||
format!(r#" id="{}""#, anchor),
|
||||
format!(r#" ID="{}""#, anchor),
|
||||
format!(" name='{}'", anchor),
|
||||
format!(" NAME='{}'", anchor),
|
||||
format!(r#" name="{}""#, anchor),
|
||||
format!(r#" NAME="{}""#, anchor),
|
||||
];
|
||||
|
||||
if checks.iter().any(|check| body[..].contains(&check[..])) {
|
||||
|
@ -134,8 +121,11 @@ fn check_page_for_anchor(url: &str, body: String) -> Result<()> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{check_page_for_anchor, check_url, has_anchor, LinkChecker, LINKS};
|
||||
use super::{
|
||||
check_page_for_anchor, check_url, has_anchor, is_valid, message, LinkChecker, LINKS,
|
||||
};
|
||||
use mockito::mock;
|
||||
use reqwest::StatusCode;
|
||||
|
||||
// NOTE: HTTP mock paths below are randomly generated to avoid name
|
||||
// collisions. Mocks with the same path can sometimes bleed between tests
|
||||
|
@ -163,7 +153,8 @@ mod tests {
|
|||
.create();
|
||||
|
||||
let res = check_url(&url, &LinkChecker::default());
|
||||
assert!(res.is_valid());
|
||||
assert!(is_valid(&res));
|
||||
assert_eq!(message(&res), "200 OK");
|
||||
assert!(LINKS.read().unwrap().get(&url).is_some());
|
||||
}
|
||||
|
||||
|
@ -183,9 +174,9 @@ mod tests {
|
|||
|
||||
let url = format!("{}{}", mockito::server_url(), "/c7qrtrv3zz");
|
||||
let res = check_url(&url, &LinkChecker::default());
|
||||
assert!(res.is_valid());
|
||||
assert!(res.code.is_some());
|
||||
assert!(res.error.is_none());
|
||||
assert!(is_valid(&res));
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(message(&res), "200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -199,9 +190,8 @@ mod tests {
|
|||
|
||||
let url = format!("{}{}", mockito::server_url(), "/C4Szbfnvj6M0LoPk");
|
||||
let res = check_url(&url, &LinkChecker::default());
|
||||
assert!(res.is_valid());
|
||||
assert!(res.code.is_some());
|
||||
assert!(res.error.is_none());
|
||||
assert!(is_valid(&res));
|
||||
assert_eq!(res.unwrap(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -221,9 +211,9 @@ mod tests {
|
|||
|
||||
let url = format!("{}{}", mockito::server_url(), "/cav9vibhsc");
|
||||
let res = check_url(&url, &LinkChecker::default());
|
||||
assert_eq!(res.is_valid(), false);
|
||||
assert!(res.code.is_none());
|
||||
assert!(res.error.is_some());
|
||||
assert!(!is_valid(&res));
|
||||
assert!(res.is_err());
|
||||
assert_eq!(message(&res), "Client error status code (404 Not Found) received");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -236,9 +226,9 @@ mod tests {
|
|||
|
||||
let url = format!("{}{}", mockito::server_url(), "/nlhab9c1vc");
|
||||
let res = check_url(&url, &LinkChecker::default());
|
||||
assert_eq!(res.is_valid(), false);
|
||||
assert!(res.code.is_none());
|
||||
assert!(res.error.is_some());
|
||||
assert!(!is_valid(&res));
|
||||
assert!(res.is_err());
|
||||
assert_eq!(message(&res), "Client error status code (404 Not Found) received");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -251,17 +241,18 @@ mod tests {
|
|||
|
||||
let url = format!("{}{}", mockito::server_url(), "/qdbrssazes");
|
||||
let res = check_url(&url, &LinkChecker::default());
|
||||
assert_eq!(res.is_valid(), false);
|
||||
assert!(res.code.is_none());
|
||||
assert!(res.error.is_some());
|
||||
assert!(!is_valid(&res));
|
||||
assert!(res.is_err());
|
||||
assert_eq!(message(&res), "Server error status code (500 Internal Server Error) received");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_fail_unresolved_links() {
|
||||
let res = check_url("https://t6l5cn9lpm.lxizfnzckd", &LinkChecker::default());
|
||||
assert_eq!(res.is_valid(), false);
|
||||
assert!(res.code.is_none());
|
||||
assert!(res.error.is_some());
|
||||
assert!(!is_valid(&res));
|
||||
assert!(res.is_err());
|
||||
assert!(message(&res)
|
||||
.starts_with("error sending request for url (https://t6l5cn9lpm.lxizfnzckd/)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -272,6 +263,15 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
// https://github.com/getzola/zola/issues/948
|
||||
#[test]
|
||||
fn can_validate_anchors_in_capital() {
|
||||
let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
|
||||
let body = r#"<body><h3 ID="method.collect">collect</h3></body>"#.to_string();
|
||||
let res = check_page_for_anchor(url, body);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_validate_anchors_with_other_quotes() {
|
||||
let url = "https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect";
|
||||
|
@ -299,29 +299,25 @@ mod tests {
|
|||
#[test]
|
||||
fn can_check_url_for_anchor() {
|
||||
let url = "https://doc.rust-lang.org/std/index.html#the-rust-standard-library";
|
||||
let res = has_anchor(url);
|
||||
assert_eq!(res, true);
|
||||
assert!(has_anchor(url));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn will_return_false_when_no_anchor() {
|
||||
let url = "https://doc.rust-lang.org/std/index.html";
|
||||
let res = has_anchor(url);
|
||||
assert_eq!(res, false);
|
||||
assert!(!has_anchor(url));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn will_return_false_when_has_router_url() {
|
||||
let url = "https://doc.rust-lang.org/#/std";
|
||||
let res = has_anchor(url);
|
||||
assert_eq!(res, false);
|
||||
assert!(!has_anchor(url));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn will_return_false_when_has_router_url_alt() {
|
||||
let url = "https://doc.rust-lang.org/#!/std";
|
||||
let res = has_anchor(url);
|
||||
assert_eq!(res, false);
|
||||
assert!(!has_anchor(url));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -347,7 +343,7 @@ mod tests {
|
|||
|
||||
// anchor check is ignored because the url matches the prefix
|
||||
let ignore = format!("{}{}", mockito::server_url(), "/ignore/i30hobj1cy#nonexistent");
|
||||
assert!(check_url(&ignore, &config).is_valid());
|
||||
assert!(is_valid(&check_url(&ignore, &config)));
|
||||
|
||||
let _m2 = mock("GET", "/guvqcqwmth")
|
||||
.with_header("Content-Type", "text/html")
|
||||
|
@ -367,9 +363,9 @@ mod tests {
|
|||
|
||||
// other anchors are checked
|
||||
let existent = format!("{}{}", mockito::server_url(), "/guvqcqwmth#existent");
|
||||
assert!(check_url(&existent, &config).is_valid());
|
||||
assert!(is_valid(&check_url(&existent, &config)));
|
||||
|
||||
let nonexistent = format!("{}{}", mockito::server_url(), "/guvqcqwmth#nonexistent");
|
||||
assert_eq!(check_url(&nonexistent, &config).is_valid(), false);
|
||||
assert!(!is_valid(&check_url(&nonexistent, &config)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -395,7 +395,17 @@ pub fn after_template_change(site: &mut Site, path: &Path) -> Result<()> {
|
|||
|
||||
match filename {
|
||||
"sitemap.xml" => site.render_sitemap(),
|
||||
"rss.xml" => site.render_rss_feed(site.library.read().unwrap().pages_values(), None),
|
||||
filename if filename == site.config.feed_filename => {
|
||||
// FIXME: this is insufficient; for multilingual sites, it’s rendering the wrong
|
||||
// content into the root feed, and it’s not regenerating any of the other feeds (other
|
||||
// languages or taxonomies with feed enabled).
|
||||
site.render_feed(
|
||||
site.library.read().unwrap().pages_values(),
|
||||
None,
|
||||
&site.config.default_language,
|
||||
None,
|
||||
)
|
||||
}
|
||||
"split_sitemap_index.xml" => site.render_sitemap(),
|
||||
"robots.txt" => site.render_robots(),
|
||||
"single.html" | "list.html" => site.render_taxonomies(),
|
||||
|
|
|
@ -20,7 +20,8 @@ macro_rules! load_and_build_site {
|
|||
dir::copy(&path, &$tmp_dir, &options).unwrap();
|
||||
|
||||
let site_path = $tmp_dir.path().join($site);
|
||||
let mut site = Site::new(&site_path, "config.toml").unwrap();
|
||||
let config_file = site_path.join("config.toml");
|
||||
let mut site = Site::new(&site_path, &config_file).unwrap();
|
||||
site.load().unwrap();
|
||||
let public = &site_path.join("public");
|
||||
site.set_output_path(&public);
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
tera = { version = "1", features = ["preserve_order"] }
|
||||
syntect = "=3.2.0"
|
||||
syntect = "4.1"
|
||||
pulldown-cmark = "0.7"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
|
|
|
@ -231,7 +231,9 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
.unwrap_or(::syntect::highlighting::Color::WHITE);
|
||||
background = IncludeBackground::IfDifferent(color);
|
||||
let snippet = start_highlighted_html_snippet(theme);
|
||||
Event::Html(snippet.0.into())
|
||||
let mut html = snippet.0;
|
||||
html.push_str("<code>");
|
||||
Event::Html(html.into())
|
||||
}
|
||||
Event::End(Tag::CodeBlock(_)) => {
|
||||
if !context.config.highlight_code {
|
||||
|
@ -239,7 +241,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
}
|
||||
// reset highlight and close the code block
|
||||
highlighter = None;
|
||||
Event::Html("</pre>".into())
|
||||
Event::Html("</code></pre>".into())
|
||||
}
|
||||
Event::Start(Tag::Image(link_type, src, title)) => {
|
||||
if is_colocated_asset_link(&src) {
|
||||
|
@ -329,6 +331,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
};
|
||||
let mut c = tera::Context::new();
|
||||
c.insert("id", &id);
|
||||
c.insert("level", &heading_ref.level);
|
||||
|
||||
let anchor_link = utils::templates::render_template(
|
||||
&ANCHOR_LINK_TEMPLATE,
|
||||
|
|
|
@ -39,7 +39,7 @@ fn can_highlight_code_block_no_lang() {
|
|||
let res = render_content("```\n$ gutenberg server\n$ ping\n```", &context).unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
"<pre style=\"background-color:#2b303b;\">\n<span style=\"color:#c0c5ce;\">$ gutenberg server\n$ ping\n</span></pre>"
|
||||
"<pre style=\"background-color:#2b303b;\">\n<code><span style=\"color:#c0c5ce;\">$ gutenberg server\n$ ping\n</span></code></pre>"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ fn can_highlight_code_block_with_lang() {
|
|||
let res = render_content("```python\nlist.append(1)\n```", &context).unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
"<pre style=\"background-color:#2b303b;\">\n<span style=\"color:#c0c5ce;\">list.</span><span style=\"color:#bf616a;\">append</span><span style=\"color:#c0c5ce;\">(</span><span style=\"color:#d08770;\">1</span><span style=\"color:#c0c5ce;\">)\n</span></pre>"
|
||||
"<pre style=\"background-color:#2b303b;\">\n<code><span style=\"color:#c0c5ce;\">list.</span><span style=\"color:#bf616a;\">append</span><span style=\"color:#c0c5ce;\">(</span><span style=\"color:#d08770;\">1</span><span style=\"color:#c0c5ce;\">)\n</span></code></pre>"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ fn can_higlight_code_block_with_unknown_lang() {
|
|||
// defaults to plain text
|
||||
assert_eq!(
|
||||
res.body,
|
||||
"<pre style=\"background-color:#2b303b;\">\n<span style=\"color:#c0c5ce;\">list.append(1)\n</span></pre>"
|
||||
"<pre style=\"background-color:#2b303b;\">\n<code><span style=\"color:#c0c5ce;\">list.append(1)\n</span></code></pre>"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ base_url = "https://replace-this-with-your-url.com"
|
|||
theme = "sample"
|
||||
|
||||
taxonomies = [
|
||||
{name = "tags", rss = true},
|
||||
{name = "tags", feed = true},
|
||||
{name = "categories"}
|
||||
]
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ fn bench_loading_small_blog(b: &mut test::Bencher) {
|
|||
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
path.push("benches");
|
||||
path.push("small-blog");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
|
||||
b.iter(|| site.load().unwrap());
|
||||
}
|
||||
|
@ -21,7 +22,8 @@ fn bench_loading_small_blog_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
path.push("benches");
|
||||
path.push("small-blog");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
site.config.highlight_code = true;
|
||||
|
||||
b.iter(|| site.load().unwrap());
|
||||
|
@ -32,7 +34,8 @@ fn bench_loading_small_blog_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("medium-blog");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
//}
|
||||
|
@ -42,7 +45,8 @@ fn bench_loading_small_blog_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("medium-blog");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
// site.config.highlight_code = true;
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
|
@ -53,7 +57,8 @@ fn bench_loading_small_blog_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("big-blog");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
//}
|
||||
|
@ -63,7 +68,8 @@ fn bench_loading_small_blog_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("big-blog");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
// site.config.highlight_code = true;
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
|
@ -74,7 +80,8 @@ fn bench_loading_small_blog_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("huge-blog");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
//}
|
||||
|
@ -84,7 +91,8 @@ fn bench_loading_small_blog_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("huge-blog");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
// site.config.highlight_code = true;
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
|
@ -95,7 +103,8 @@ fn bench_loading_small_kb(b: &mut test::Bencher) {
|
|||
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
path.push("benches");
|
||||
path.push("small-kb");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
|
||||
b.iter(|| site.load().unwrap());
|
||||
}
|
||||
|
@ -105,7 +114,8 @@ fn bench_loading_small_kb_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
path.push("benches");
|
||||
path.push("small-kb");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
site.config.highlight_code = true;
|
||||
|
||||
b.iter(|| site.load().unwrap());
|
||||
|
@ -116,7 +126,8 @@ fn bench_loading_small_kb_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("medium-kb");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
//}
|
||||
|
@ -126,7 +137,8 @@ fn bench_loading_small_kb_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("medium-kb");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
// site.config.highlight_code = Some(true);
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
|
@ -137,7 +149,8 @@ fn bench_loading_small_kb_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("huge-kb");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
//}
|
||||
|
@ -147,7 +160,8 @@ fn bench_loading_small_kb_with_syntax_highlighting(b: &mut test::Bencher) {
|
|||
// let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
// path.push("benches");
|
||||
// path.push("huge-kb");
|
||||
// let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
// let config_file = path.join("config.toml");
|
||||
// let mut site = Site::new(&path, &config_file).unwrap();
|
||||
// site.config.highlight_code = Some(true);
|
||||
//
|
||||
// b.iter(|| site.load().unwrap());
|
||||
|
|
|
@ -11,7 +11,8 @@ fn setup_site(name: &str) -> Site {
|
|||
let mut path = env::current_dir().unwrap().to_path_buf();
|
||||
path.push("benches");
|
||||
path.push(name);
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
site.load().unwrap();
|
||||
site
|
||||
}
|
||||
|
@ -35,12 +36,20 @@ fn bench_render_sitemap(b: &mut test::Bencher) {
|
|||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_render_rss_feed(b: &mut test::Bencher) {
|
||||
fn bench_render_feed(b: &mut test::Bencher) {
|
||||
let mut site = setup_site("big-blog");
|
||||
let tmp_dir = tempdir().expect("create temp dir");
|
||||
let public = &tmp_dir.path().join("public");
|
||||
site.set_output_path(&public);
|
||||
b.iter(|| site.render_rss_feed(site.library.read().unwrap().pages_values(), None).unwrap());
|
||||
b.iter(|| {
|
||||
site.render_feed(
|
||||
site.library.read().unwrap().pages_values(),
|
||||
None,
|
||||
&site.config.default_language,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
|
|
@ -8,15 +8,16 @@ use std::sync::{Arc, Mutex, RwLock};
|
|||
use glob::glob;
|
||||
use rayon::prelude::*;
|
||||
use sass_rs::{compile_file, Options as SassOptions, OutputStyle};
|
||||
use serde_derive::Serialize;
|
||||
use tera::{Context, Tera};
|
||||
|
||||
use config::{get_config, Config};
|
||||
use config::{get_config, Config, Taxonomy as TaxonomyConfig};
|
||||
use errors::{bail, Error, ErrorKind, Result};
|
||||
use front_matter::InsertAnchor;
|
||||
use library::{
|
||||
find_taxonomies, sort_actual_pages_by_date, Library, Page, Paginator, Section, Taxonomy,
|
||||
TaxonomyItem,
|
||||
};
|
||||
use link_checker::check_url;
|
||||
use templates::{global_fns, render_redirect_template, ZOLA_TERA};
|
||||
use utils::fs::{copy_directory, create_directory, create_file, ensure_directory_exists};
|
||||
use utils::net::get_available_port;
|
||||
|
@ -45,12 +46,26 @@ pub struct Site {
|
|||
include_drafts: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
struct SerializedTaxonomyItem<'a> {
|
||||
name: &'a str,
|
||||
slug: &'a str,
|
||||
permalink: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> SerializedTaxonomyItem<'a> {
|
||||
pub fn from_item(item: &'a TaxonomyItem) -> Self {
|
||||
SerializedTaxonomyItem { name: &item.name, slug: &item.slug, permalink: &item.permalink }
|
||||
}
|
||||
}
|
||||
|
||||
impl Site {
|
||||
/// Parse a site at the given path. Defaults to the current dir
|
||||
/// Passing in a path is used in tests and when --root argument is passed
|
||||
pub fn new<P: AsRef<Path>>(path: P, config_file: &str) -> Result<Site> {
|
||||
pub fn new<P: AsRef<Path>, P2: AsRef<Path>>(path: P, config_file: P2) -> Result<Site> {
|
||||
let path = path.as_ref();
|
||||
let mut config = get_config(path, config_file);
|
||||
let config_file = config_file.as_ref();
|
||||
let mut config = get_config(config_file);
|
||||
config.load_extra_syntaxes(path)?;
|
||||
|
||||
let tpl_glob =
|
||||
|
@ -396,8 +411,8 @@ impl Site {
|
|||
{
|
||||
return None;
|
||||
}
|
||||
let res = check_url(&link, &self.config.link_checker);
|
||||
if res.is_valid() {
|
||||
let res = link_checker::check_url(&link, &self.config.link_checker);
|
||||
if link_checker::is_valid(&res) {
|
||||
None
|
||||
} else {
|
||||
Some((page_path, link, res))
|
||||
|
@ -423,7 +438,7 @@ impl Site {
|
|||
"Dead link in {} to {}: {}",
|
||||
page_path.to_string_lossy(),
|
||||
link,
|
||||
check_res.message()
|
||||
link_checker::message(&check_res)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
@ -458,13 +473,14 @@ impl Site {
|
|||
let filename = format!("_index.{}.md", l);
|
||||
index_section.file.path = self.content_path.join(&filename);
|
||||
index_section.file.relative = filename;
|
||||
index_section.lang = index_section.file.find_language(&self.config)?;
|
||||
} else {
|
||||
index_section.file.name = "_index".to_string();
|
||||
index_section.permalink = self.config.make_permalink("");
|
||||
index_section.file.path = self.content_path.join("_index.md");
|
||||
index_section.file.relative = "_index.md".to_string();
|
||||
index_section.path = "/".to_string();
|
||||
}
|
||||
index_section.lang = index_section.file.find_language(&self.config)?;
|
||||
library.insert_section(index_section);
|
||||
}
|
||||
}
|
||||
|
@ -518,7 +534,7 @@ impl Site {
|
|||
pub fn register_early_global_fns(&mut self) {
|
||||
self.tera.register_function(
|
||||
"get_url",
|
||||
global_fns::GetUrl::new(self.config.clone(), self.permalinks.clone()),
|
||||
global_fns::GetUrl::new(self.config.clone(), self.permalinks.clone(), self.content_path.clone()),
|
||||
);
|
||||
self.tera.register_function(
|
||||
"resize_image",
|
||||
|
@ -626,15 +642,15 @@ impl Site {
|
|||
}
|
||||
|
||||
/// Inject live reload script tag if in live reload mode
|
||||
fn inject_livereload(&self, html: String) -> String {
|
||||
fn inject_livereload(&self, mut html: String) -> String {
|
||||
if let Some(port) = self.live_reload {
|
||||
return html.replace(
|
||||
"</body>",
|
||||
&format!(
|
||||
r#"<script src="/livereload.js?port={}&mindelay=10"></script></body>"#,
|
||||
port
|
||||
),
|
||||
);
|
||||
let script =
|
||||
format!(r#"<script src="/livereload.js?port={}&mindelay=10"></script>"#, port,);
|
||||
if let Some(index) = html.rfind("</body>") {
|
||||
html.insert_str(index, &script);
|
||||
} else {
|
||||
html.push_str(&script);
|
||||
}
|
||||
}
|
||||
|
||||
html
|
||||
|
@ -743,8 +759,9 @@ impl Site {
|
|||
self.render_sitemap()?;
|
||||
|
||||
let library = self.library.read().unwrap();
|
||||
if self.config.generate_rss {
|
||||
let pages = if self.config.is_multilingual() {
|
||||
if self.config.generate_feed {
|
||||
let is_multilingual = self.config.is_multilingual();
|
||||
let pages = if is_multilingual {
|
||||
library
|
||||
.pages_values()
|
||||
.iter()
|
||||
|
@ -754,16 +771,16 @@ impl Site {
|
|||
} else {
|
||||
library.pages_values()
|
||||
};
|
||||
self.render_rss_feed(pages, None)?;
|
||||
self.render_feed(pages, None, &self.config.default_language, None)?;
|
||||
}
|
||||
|
||||
for lang in &self.config.languages {
|
||||
if !lang.rss {
|
||||
if !lang.feed {
|
||||
continue;
|
||||
}
|
||||
let pages =
|
||||
library.pages_values().iter().filter(|p| p.lang == lang.code).cloned().collect();
|
||||
self.render_rss_feed(pages, Some(&PathBuf::from(lang.code.clone())))?;
|
||||
self.render_feed(pages, Some(&PathBuf::from(lang.code.clone())), &lang.code, None)?;
|
||||
}
|
||||
|
||||
self.render_404()?;
|
||||
|
@ -981,10 +998,16 @@ impl Site {
|
|||
create_file(&path.join("index.html"), &self.inject_livereload(single_output))?;
|
||||
}
|
||||
|
||||
if taxonomy.kind.rss {
|
||||
self.render_rss_feed(
|
||||
if taxonomy.kind.feed {
|
||||
self.render_feed(
|
||||
item.pages.iter().map(|p| library.get_page_by_key(*p)).collect(),
|
||||
Some(&PathBuf::from(format!("{}/{}", taxonomy.kind.name, item.slug))),
|
||||
if self.config.is_multilingual() && !taxonomy.kind.lang.is_empty() {
|
||||
&taxonomy.kind.lang
|
||||
} else {
|
||||
&self.config.default_language
|
||||
},
|
||||
Some((&taxonomy.kind, &item)),
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -1043,30 +1066,40 @@ impl Site {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Renders a RSS feed for the given path and at the given path
|
||||
/// If both arguments are `None`, it will render only the RSS feed for the whole
|
||||
/// Renders a feed for the given path and at the given path
|
||||
/// If both arguments are `None`, it will render only the feed for the whole
|
||||
/// site at the root folder.
|
||||
pub fn render_rss_feed(
|
||||
pub fn render_feed(
|
||||
&self,
|
||||
all_pages: Vec<&Page>,
|
||||
base_path: Option<&PathBuf>,
|
||||
lang: &str,
|
||||
taxonomy_and_item: Option<(&TaxonomyConfig, &TaxonomyItem)>,
|
||||
) -> Result<()> {
|
||||
ensure_directory_exists(&self.output_path)?;
|
||||
|
||||
let mut context = Context::new();
|
||||
let mut pages = all_pages.into_iter().filter(|p| p.meta.date.is_some()).collect::<Vec<_>>();
|
||||
|
||||
// Don't generate a RSS feed if none of the pages has a date
|
||||
// Don't generate a feed if none of the pages has a date
|
||||
if pages.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pages.par_sort_unstable_by(sort_actual_pages_by_date);
|
||||
|
||||
context.insert("last_build_date", &pages[0].meta.date.clone());
|
||||
context.insert(
|
||||
"last_updated",
|
||||
pages
|
||||
.iter()
|
||||
.filter_map(|page| page.meta.updated.as_ref())
|
||||
.chain(pages[0].meta.date.as_ref())
|
||||
.max() // I love lexicographically sorted date strings
|
||||
.unwrap(), // Guaranteed because of pages[0].meta.date
|
||||
);
|
||||
let library = self.library.read().unwrap();
|
||||
// limit to the last n elements if the limit is set; otherwise use all.
|
||||
let num_entries = self.config.rss_limit.unwrap_or_else(|| pages.len());
|
||||
let num_entries = self.config.feed_limit.unwrap_or_else(|| pages.len());
|
||||
let p = pages
|
||||
.iter()
|
||||
.take(num_entries)
|
||||
|
@ -1075,16 +1108,24 @@ impl Site {
|
|||
|
||||
context.insert("pages", &p);
|
||||
context.insert("config", &self.config);
|
||||
context.insert("lang", lang);
|
||||
|
||||
let rss_feed_url = if let Some(ref base) = base_path {
|
||||
self.config.make_permalink(&base.join("rss.xml").to_string_lossy().replace('\\', "/"))
|
||||
let feed_filename = &self.config.feed_filename;
|
||||
let feed_url = if let Some(ref base) = base_path {
|
||||
self.config
|
||||
.make_permalink(&base.join(feed_filename).to_string_lossy().replace('\\', "/"))
|
||||
} else {
|
||||
self.config.make_permalink("rss.xml")
|
||||
self.config.make_permalink(feed_filename)
|
||||
};
|
||||
|
||||
context.insert("feed_url", &rss_feed_url);
|
||||
context.insert("feed_url", &feed_url);
|
||||
|
||||
let feed = &render_template("rss.xml", &self.tera, context, &self.config.theme)?;
|
||||
if let Some((taxonomy, item)) = taxonomy_and_item {
|
||||
context.insert("taxonomy", taxonomy);
|
||||
context.insert("term", &SerializedTaxonomyItem::from_item(item));
|
||||
}
|
||||
|
||||
let feed = &render_template(feed_filename, &self.tera, context, &self.config.theme)?;
|
||||
|
||||
if let Some(ref base) = base_path {
|
||||
let mut output_path = self.output_path.clone();
|
||||
|
@ -1094,9 +1135,9 @@ impl Site {
|
|||
create_directory(&output_path)?;
|
||||
}
|
||||
}
|
||||
create_file(&output_path.join("rss.xml"), feed)?;
|
||||
create_file(&output_path.join(feed_filename), feed)?;
|
||||
} else {
|
||||
create_file(&self.output_path.join("rss.xml"), feed)?;
|
||||
create_file(&self.output_path.join(feed_filename), feed)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use tera::{Map, Value};
|
|||
#[derive(Debug, Serialize)]
|
||||
pub struct SitemapEntry<'a> {
|
||||
pub permalink: Cow<'a, str>,
|
||||
pub date: Option<String>,
|
||||
pub updated: Option<String>,
|
||||
pub extra: Option<&'a Map<String, Value>>,
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,8 @@ impl<'a> PartialEq for SitemapEntry<'a> {
|
|||
impl<'a> Eq for SitemapEntry<'a> {}
|
||||
|
||||
impl<'a> SitemapEntry<'a> {
|
||||
pub fn new(permalink: Cow<'a, str>, date: Option<String>) -> Self {
|
||||
SitemapEntry { permalink, date, extra: None }
|
||||
pub fn new(permalink: Cow<'a, str>, updated: Option<String>) -> Self {
|
||||
SitemapEntry { permalink, updated, extra: None }
|
||||
}
|
||||
|
||||
pub fn add_extra(&mut self, extra: &'a Map<String, Value>) {
|
||||
|
@ -65,11 +65,10 @@ pub fn find_entries<'a>(
|
|||
.pages_values()
|
||||
.iter()
|
||||
.map(|p| {
|
||||
let date = match p.meta.date {
|
||||
Some(ref d) => Some(d.to_string()),
|
||||
None => None,
|
||||
};
|
||||
let mut entry = SitemapEntry::new(Cow::Borrowed(&p.permalink), date);
|
||||
let mut entry = SitemapEntry::new(
|
||||
Cow::Borrowed(&p.permalink),
|
||||
p.meta.updated.clone().or_else(|| p.meta.date.clone()),
|
||||
);
|
||||
entry.add_extra(&p.meta.extra);
|
||||
entry
|
||||
})
|
||||
|
|
|
@ -38,7 +38,8 @@ macro_rules! file_contains {
|
|||
pub fn build_site(name: &str) -> (Site, TempDir, PathBuf) {
|
||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||
path.push(name);
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
site.load().unwrap();
|
||||
let tmp_dir = tempdir().expect("create temp dir");
|
||||
let public = &tmp_dir.path().join("public");
|
||||
|
@ -54,7 +55,8 @@ where
|
|||
{
|
||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||
path.push(name);
|
||||
let site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let site = Site::new(&path, &config_file).unwrap();
|
||||
let (mut site, needs_loading) = setup_cb(site);
|
||||
if needs_loading {
|
||||
site.load().unwrap();
|
||||
|
|
|
@ -13,7 +13,8 @@ use site::Site;
|
|||
fn can_parse_site() {
|
||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||
path.push("test_site");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
site.load().unwrap();
|
||||
let library = site.library.read().unwrap();
|
||||
|
||||
|
@ -152,7 +153,7 @@ fn can_build_site_without_live_reload() {
|
|||
// We do have categories
|
||||
assert_eq!(file_exists!(public, "categories/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/rss.xml"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/atom.xml"), true);
|
||||
// But no tags
|
||||
assert_eq!(file_exists!(public, "tags/index.html"), false);
|
||||
|
||||
|
@ -232,7 +233,7 @@ fn can_build_site_with_live_reload_and_drafts() {
|
|||
// We do have categories
|
||||
assert_eq!(file_exists!(public, "categories/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/rss.xml"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/atom.xml"), true);
|
||||
// But no tags
|
||||
assert_eq!(file_exists!(public, "tags/index.html"), false);
|
||||
|
||||
|
@ -294,11 +295,11 @@ fn can_build_site_with_taxonomies() {
|
|||
assert!(file_exists!(public, "categories/index.html"));
|
||||
assert!(file_exists!(public, "categories/a/index.html"));
|
||||
assert!(file_exists!(public, "categories/b/index.html"));
|
||||
assert!(file_exists!(public, "categories/a/rss.xml"));
|
||||
assert!(file_exists!(public, "categories/a/atom.xml"));
|
||||
assert!(file_contains!(
|
||||
public,
|
||||
"categories/a/rss.xml",
|
||||
"https://replace-this-with-your-url.com/categories/a/rss.xml"
|
||||
"categories/a/atom.xml",
|
||||
"https://replace-this-with-your-url.com/categories/a/atom.xml"
|
||||
));
|
||||
// Extending from a theme works
|
||||
assert!(file_contains!(public, "categories/a/index.html", "EXTENDED"));
|
||||
|
@ -513,7 +514,7 @@ fn can_build_site_with_pagination_for_taxonomy() {
|
|||
name: "tags".to_string(),
|
||||
paginate_by: Some(2),
|
||||
paginate_path: None,
|
||||
rss: true,
|
||||
feed: true,
|
||||
lang: site.config.default_language.clone(),
|
||||
});
|
||||
site.load().unwrap();
|
||||
|
@ -547,9 +548,9 @@ fn can_build_site_with_pagination_for_taxonomy() {
|
|||
|
||||
// Tags
|
||||
assert!(file_exists!(public, "tags/index.html"));
|
||||
// With RSS
|
||||
assert!(file_exists!(public, "tags/a/rss.xml"));
|
||||
assert!(file_exists!(public, "tags/b/rss.xml"));
|
||||
// With Atom
|
||||
assert!(file_exists!(public, "tags/a/atom.xml"));
|
||||
assert!(file_exists!(public, "tags/b/atom.xml"));
|
||||
// And pagination!
|
||||
assert!(file_exists!(public, "tags/a/page/1/index.html"));
|
||||
assert!(file_exists!(public, "tags/b/page/1/index.html"));
|
||||
|
@ -588,15 +589,15 @@ fn can_build_site_with_pagination_for_taxonomy() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_build_rss_feed() {
|
||||
fn can_build_feed() {
|
||||
let (_, _tmp_dir, public) = build_site("test_site");
|
||||
|
||||
assert!(&public.exists());
|
||||
assert!(file_exists!(public, "rss.xml"));
|
||||
assert!(file_exists!(public, "atom.xml"));
|
||||
// latest article is posts/extra-syntax.md
|
||||
assert!(file_contains!(public, "rss.xml", "Extra Syntax"));
|
||||
assert!(file_contains!(public, "atom.xml", "Extra Syntax"));
|
||||
// Next is posts/simple.md
|
||||
assert!(file_contains!(public, "rss.xml", "Simple article with shortcodes"));
|
||||
assert!(file_contains!(public, "atom.xml", "Simple article with shortcodes"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -628,7 +629,8 @@ fn can_build_with_extra_syntaxes() {
|
|||
fn can_apply_page_templates() {
|
||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||
path.push("test_site");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
site.load().unwrap();
|
||||
|
||||
let template_path = path.join("content").join("applying_page_template");
|
||||
|
|
|
@ -9,7 +9,8 @@ use site::Site;
|
|||
fn can_parse_multilingual_site() {
|
||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||
path.push("test_site_i18n");
|
||||
let mut site = Site::new(&path, "config.toml").unwrap();
|
||||
let config_file = path.join("config.toml");
|
||||
let mut site = Site::new(&path, &config_file).unwrap();
|
||||
site.load().unwrap();
|
||||
|
||||
let library = site.library.read().unwrap();
|
||||
|
@ -115,15 +116,25 @@ fn can_build_multilingual_site() {
|
|||
assert!(file_contains!(public, "sitemap.xml", "https://example.com/fr/blog/something-else/"));
|
||||
assert!(file_contains!(public, "sitemap.xml", "https://example.com/it/blog/something-else/"));
|
||||
|
||||
// one rss per language
|
||||
assert!(file_exists!(public, "rss.xml"));
|
||||
assert!(file_contains!(public, "rss.xml", "https://example.com/blog/something-else/"));
|
||||
assert!(!file_contains!(public, "rss.xml", "https://example.com/fr/blog/something-else/"));
|
||||
assert!(file_exists!(public, "fr/rss.xml"));
|
||||
assert!(!file_contains!(public, "fr/rss.xml", "https://example.com/blog/something-else/"));
|
||||
assert!(file_contains!(public, "fr/rss.xml", "https://example.com/fr/blog/something-else/"));
|
||||
// Italian doesn't have RSS enabled
|
||||
assert!(!file_exists!(public, "it/rss.xml"));
|
||||
// one feed per language
|
||||
assert!(file_exists!(public, "atom.xml"));
|
||||
assert!(file_contains!(public, "atom.xml", "https://example.com/blog/something-else/"));
|
||||
assert!(!file_contains!(public, "atom.xml", "https://example.com/fr/blog/something-else/"));
|
||||
assert!(file_contains!(
|
||||
public,
|
||||
"atom.xml",
|
||||
r#"<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">"#
|
||||
));
|
||||
assert!(file_exists!(public, "fr/atom.xml"));
|
||||
assert!(!file_contains!(public, "fr/atom.xml", "https://example.com/blog/something-else/"));
|
||||
assert!(file_contains!(public, "fr/atom.xml", "https://example.com/fr/blog/something-else/"));
|
||||
assert!(file_contains!(
|
||||
public,
|
||||
"fr/atom.xml",
|
||||
r#"<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">"#
|
||||
));
|
||||
// Italian doesn't have feed enabled
|
||||
assert!(!file_exists!(public, "it/atom.xml"));
|
||||
|
||||
// Taxonomies are per-language
|
||||
// English
|
||||
|
@ -131,7 +142,17 @@ fn can_build_multilingual_site() {
|
|||
assert!(file_contains!(public, "authors/index.html", "Queen"));
|
||||
assert!(!file_contains!(public, "authors/index.html", "Vincent"));
|
||||
assert!(!file_exists!(public, "auteurs/index.html"));
|
||||
assert!(file_exists!(public, "authors/queen-elizabeth/rss.xml"));
|
||||
assert!(file_exists!(public, "authors/queen-elizabeth/atom.xml"));
|
||||
assert!(file_contains!(
|
||||
public,
|
||||
"authors/queen-elizabeth/atom.xml",
|
||||
r#"<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">"#
|
||||
));
|
||||
assert!(file_contains!(
|
||||
public,
|
||||
"authors/queen-elizabeth/atom.xml",
|
||||
r#"<title> - Queen Elizabeth</title>"#
|
||||
));
|
||||
|
||||
assert!(file_exists!(public, "tags/index.html"));
|
||||
assert!(file_contains!(public, "tags/index.html", "hello"));
|
||||
|
@ -142,7 +163,7 @@ fn can_build_multilingual_site() {
|
|||
assert!(file_exists!(public, "fr/auteurs/index.html"));
|
||||
assert!(!file_contains!(public, "fr/auteurs/index.html", "Queen"));
|
||||
assert!(file_contains!(public, "fr/auteurs/index.html", "Vincent"));
|
||||
assert!(!file_exists!(public, "fr/auteurs/vincent-prouillet/rss.xml"));
|
||||
assert!(!file_exists!(public, "fr/auteurs/vincent-prouillet/atom.xml"));
|
||||
|
||||
assert!(file_exists!(public, "fr/tags/index.html"));
|
||||
assert!(file_contains!(public, "fr/tags/index.html", "bonjour"));
|
||||
|
|
|
@ -13,7 +13,7 @@ toml = "0.5"
|
|||
csv = "1"
|
||||
image = "0.23"
|
||||
serde_json = "1.0"
|
||||
reqwest = { version = "0.10", features = ["blocking"] }
|
||||
sha2 = "0.8"
|
||||
url = "2"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
|
@ -22,5 +22,10 @@ library = { path = "../library" }
|
|||
config = { path = "../config" }
|
||||
imageproc = { path = "../imageproc" }
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["blocking", "rustls-tls"]
|
||||
|
||||
[dev-dependencies]
|
||||
mockito = "0.23"
|
||||
mockito = "0.25"
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>File Not Found: 404.</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Oops!</h1>
|
||||
<h2>File Not Found: 404.</h2>
|
||||
</body>
|
||||
</html>
|
||||
<title>404 Not Found</title>
|
||||
<h1>404 Not Found</h1>
|
||||
|
|
25
components/templates/src/builtins/atom.xml
Normal file
25
components/templates/src/builtins/atom.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}">
|
||||
<title>{{ config.title }}
|
||||
{%- if term %} - {{ term.name }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
{%- if config.description %}
|
||||
<subtitle>{{ config.description }}</subtitle>
|
||||
{%- endif %}
|
||||
<link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/>
|
||||
<link href="{{ config.base_url | safe }}"/>
|
||||
<generator uri="https://www.getzola.org/">Zola</generator>
|
||||
<updated>{{ last_updated | date(format="%+") }}</updated>
|
||||
<id>{{ feed_url | safe }}</id>
|
||||
{%- for page in pages %}
|
||||
<entry xml:lang="{{ page.lang }}">
|
||||
<title>{{ page.title }}</title>
|
||||
<published>{{ page.date | date(format="%+") }}</published>
|
||||
<updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated>
|
||||
<link href="{{ page.permalink | safe }}" type="text/html"/>
|
||||
<id>{{ page.permalink | safe }}</id>
|
||||
<content type="html">{{ page.content }}</content>
|
||||
</entry>
|
||||
{%- endfor %}
|
||||
</feed>
|
|
@ -1,12 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redirect</title>
|
||||
<link rel="canonical" href="{{ url | safe }}" />
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="refresh" content="0;url={{ url | safe }}" />
|
||||
</head>
|
||||
<body>
|
||||
<p><a href="{{ url | safe }}">Click here</a> to be redirected.</p>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<link rel="canonical" href="{{ url | safe }}">
|
||||
<meta http-equiv="refresh" content="0;url={{ url | safe }}">
|
||||
<title>Redirect</title>
|
||||
<p><a href="{{ url | safe }}">Click here</a> to be redirected.</p>
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
<generator>Zola</generator>
|
||||
<language>{{ config.default_language }}</language>
|
||||
<atom:link href="{{ feed_url | safe }}" rel="self" type="application/rss+xml"/>
|
||||
<lastBuildDate>{{ last_build_date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
|
||||
{% for page in pages %}
|
||||
<item>
|
||||
<title>{{ page.title }}</title>
|
||||
<pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
|
||||
<link>{{ page.permalink | escape_xml | safe }}</link>
|
||||
<guid>{{ page.permalink | escape_xml | safe }}</guid>
|
||||
<description>{% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %}</description>
|
||||
</item>
|
||||
{% endfor %}
|
||||
<lastBuildDate>{{ last_updated | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
|
||||
{%- for page in pages %}
|
||||
<item>
|
||||
<title>{{ page.title }}</title>
|
||||
<pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
|
||||
<link>{{ page.permalink | escape_xml | safe }}</link>
|
||||
<guid>{{ page.permalink | escape_xml | safe }}</guid>
|
||||
<description>{% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %}</description>
|
||||
</item>
|
||||
{%- endfor %}
|
||||
</channel>
|
||||
</rss>
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
<div {% if class %}class="{{class}}"{% endif %}>
|
||||
<iframe src="https://www.streamable.com/e/{{id}}"
|
||||
scrolling="no"
|
||||
frameborder="0"
|
||||
allowfullscreen mozallowfullscreen webkitallowfullscreen>
|
||||
</iframe>
|
||||
<iframe src="https://www.streamable.com/e/{{id}}" scrolling="no" frameborder="0" allowfullscreen mozallowfullscreen webkitallowfullscreen></iframe>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<div {% if class %}class="{{class}}"{% endif %}>
|
||||
<iframe src="//player.vimeo.com/video/{{id}}" webkitallowfullscreen mozallowfullscreen allowfullscreen>
|
||||
</iframe>
|
||||
<iframe src="//player.vimeo.com/video/{{id}}" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<div {% if class %}class="{{class}}"{% endif %}>
|
||||
<iframe src="https://www.youtube.com/embed/{{id}}{% if autoplay %}?autoplay=1{% endif %}" webkitallowfullscreen mozallowfullscreen allowfullscreen>
|
||||
</iframe>
|
||||
<iframe src="https://www.youtube.com/embed/{{id}}{% if autoplay %}?autoplay=1{% endif %}" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{% for sitemap_entry in entries %}
|
||||
<url>
|
||||
<loc>{{ sitemap_entry.permalink | escape_xml | safe }}</loc>
|
||||
{% if sitemap_entry.date %}
|
||||
<lastmod>{{ sitemap_entry.date }}</lastmod>
|
||||
{% endif %}
|
||||
</url>
|
||||
{% endfor %}
|
||||
{%- for sitemap_entry in entries %}
|
||||
<url>
|
||||
<loc>{{ sitemap_entry.permalink | escape_xml | safe }}</loc>
|
||||
{%- if sitemap_entry.updated %}
|
||||
<lastmod>{{ sitemap_entry.updated }}</lastmod>
|
||||
{%- endif %}
|
||||
</url>
|
||||
{%- endfor %}
|
||||
</urlset>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{% for sitemap in sitemaps %}
|
||||
<sitemap>
|
||||
<loc>{{ sitemap }}</loc>
|
||||
</sitemap>
|
||||
{% endfor %}
|
||||
</sitemapindex>
|
||||
{%- for sitemap in sitemaps %}
|
||||
<sitemap>
|
||||
<loc>{{ sitemap }}</loc>
|
||||
</sitemap>
|
||||
{%- endfor %}
|
||||
</sitemapindex>
|
||||
|
|
|
@ -210,11 +210,10 @@ impl TeraFn for LoadData {
|
|||
.send()
|
||||
.and_then(|res| res.error_for_status())
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to request {}: {}",
|
||||
url,
|
||||
e.status().expect("response status")
|
||||
)
|
||||
match e.status() {
|
||||
Some(status) => format!("Failed to request {}: {}", url, status),
|
||||
None => format!("Could not get response status for url: {}", url),
|
||||
}
|
||||
})?;
|
||||
response
|
||||
.text()
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::{fs, io, result};
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
use tera::{from_value, to_value, Error, Function as TeraFn, Result, Value};
|
||||
|
||||
use config::Config;
|
||||
|
@ -47,12 +49,38 @@ impl TeraFn for Trans {
|
|||
pub struct GetUrl {
|
||||
config: Config,
|
||||
permalinks: HashMap<String, String>,
|
||||
content_path: PathBuf,
|
||||
}
|
||||
impl GetUrl {
|
||||
pub fn new(config: Config, permalinks: HashMap<String, String>) -> Self {
|
||||
Self { config, permalinks }
|
||||
pub fn new(config: Config, permalinks: HashMap<String, String>, content_path: PathBuf) -> Self {
|
||||
Self { config, permalinks, content_path }
|
||||
}
|
||||
}
|
||||
|
||||
fn make_path_with_lang(path: String, lang: &str, config: &Config) -> Result<String> {
|
||||
if lang == &config.default_language {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
if !config.languages.iter().any(|x| x.code == lang) {
|
||||
return Err(
|
||||
format!("`{}` is not an authorized language (check config.languages).", lang).into()
|
||||
);
|
||||
}
|
||||
|
||||
let mut splitted_path: Vec<String> = path.split(".").map(String::from).collect();
|
||||
let ilast = splitted_path.len() - 1;
|
||||
splitted_path[ilast] = format!("{}.{}", lang, splitted_path[ilast]);
|
||||
Ok(splitted_path.join("."))
|
||||
}
|
||||
|
||||
fn compute_file_sha256(path: &PathBuf) -> result::Result<String, io::Error> {
|
||||
let mut file = fs::File::open(path)?;
|
||||
let mut hasher = Sha256::new();
|
||||
io::copy(&mut file, &mut hasher)?;
|
||||
Ok(format!("{:x}", hasher.result()))
|
||||
}
|
||||
|
||||
impl TeraFn for GetUrl {
|
||||
fn call(&self, args: &HashMap<String, Value>) -> Result<Value> {
|
||||
let cachebust =
|
||||
|
@ -67,11 +95,21 @@ impl TeraFn for GetUrl {
|
|||
args.get("path"),
|
||||
"`get_url` requires a `path` argument with a string value"
|
||||
);
|
||||
|
||||
let lang = optional_arg!(String, args.get("lang"), "`get_url`: `lang` must be a string.")
|
||||
.unwrap_or_else(|| self.config.default_language.clone());
|
||||
|
||||
if path.starts_with("@/") {
|
||||
match resolve_internal_link(&path, &self.permalinks) {
|
||||
let path_with_lang = match make_path_with_lang(path, &lang, &self.config) {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
match resolve_internal_link(&path_with_lang, &self.permalinks) {
|
||||
Ok(resolved) => Ok(to_value(resolved.permalink).unwrap()),
|
||||
Err(_) => {
|
||||
Err(format!("Could not resolve URL for link `{}` not found.", path).into())
|
||||
Err(format!("Could not resolve URL for link `{}` not found.", path_with_lang)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -82,7 +120,11 @@ impl TeraFn for GetUrl {
|
|||
}
|
||||
|
||||
if cachebust {
|
||||
permalink = format!("{}?t={}", permalink, self.config.build_timestamp.unwrap());
|
||||
let full_path = self.content_path.join(&path);
|
||||
permalink = match compute_file_sha256(&full_path) {
|
||||
Ok(digest) => format!("{}?h={}", permalink, digest),
|
||||
Err(_) => return Err(format!("Could not read file `{}`. Expected location: {}", path, full_path.to_str().unwrap()).into()),
|
||||
};
|
||||
}
|
||||
Ok(to_value(permalink).unwrap())
|
||||
}
|
||||
|
@ -340,28 +382,56 @@ mod tests {
|
|||
use super::{GetTaxonomy, GetTaxonomyUrl, GetUrl, Trans};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env::temp_dir;
|
||||
use std::fs::remove_dir_all;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use tera::{to_value, Function, Value};
|
||||
|
||||
use config::{Config, Taxonomy as TaxonomyConfig};
|
||||
use library::{Library, Taxonomy, TaxonomyItem};
|
||||
use utils::fs::{create_directory, create_file};
|
||||
use utils::slugs::SlugifyStrategy;
|
||||
|
||||
struct TestContext {
|
||||
content_path: PathBuf,
|
||||
}
|
||||
impl TestContext {
|
||||
fn setup() -> Self {
|
||||
let dir = temp_dir().join("test_global_fns");
|
||||
create_directory(&dir).expect("Could not create test directory");
|
||||
create_file(&dir.join("app.css"), "// Hello world!")
|
||||
.expect("Could not create test content (app.css)");
|
||||
Self { content_path: dir }
|
||||
}
|
||||
}
|
||||
impl Drop for TestContext {
|
||||
fn drop(&mut self) {
|
||||
remove_dir_all(&self.content_path).expect("Could not free test directory");
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref TEST_CONTEXT: TestContext = TestContext::setup();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_cachebust_to_url() {
|
||||
let config = Config::default();
|
||||
let static_fn = GetUrl::new(config, HashMap::new());
|
||||
let static_fn = GetUrl::new(config, HashMap::new(), TEST_CONTEXT.content_path.clone());
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("app.css").unwrap());
|
||||
args.insert("cachebust".to_string(), to_value(true).unwrap());
|
||||
assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css?t=1");
|
||||
assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css?h=572e691dc68c3fcd653ae463261bdb38f35dc6f01715d9ce68799319dd158840");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_trailing_slashes() {
|
||||
let config = Config::default();
|
||||
let static_fn = GetUrl::new(config, HashMap::new());
|
||||
let static_fn = GetUrl::new(config, HashMap::new(), TEST_CONTEXT.content_path.clone());
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("app.css").unwrap());
|
||||
args.insert("trailing_slash".to_string(), to_value(true).unwrap());
|
||||
|
@ -371,18 +441,18 @@ mod tests {
|
|||
#[test]
|
||||
fn can_add_slashes_and_cachebust() {
|
||||
let config = Config::default();
|
||||
let static_fn = GetUrl::new(config, HashMap::new());
|
||||
let static_fn = GetUrl::new(config, HashMap::new(), TEST_CONTEXT.content_path.clone());
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("app.css").unwrap());
|
||||
args.insert("trailing_slash".to_string(), to_value(true).unwrap());
|
||||
args.insert("cachebust".to_string(), to_value(true).unwrap());
|
||||
assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css/?t=1");
|
||||
assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css/?h=572e691dc68c3fcd653ae463261bdb38f35dc6f01715d9ce68799319dd158840");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_link_to_some_static_file() {
|
||||
let config = Config::default();
|
||||
let static_fn = GetUrl::new(config, HashMap::new());
|
||||
let static_fn = GetUrl::new(config, HashMap::new(), TEST_CONTEXT.content_path.clone());
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("app.css").unwrap());
|
||||
assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css");
|
||||
|
@ -516,6 +586,9 @@ mod tests {
|
|||
const TRANS_CONFIG: &str = r#"
|
||||
base_url = "https://remplace-par-ton-url.fr"
|
||||
default_language = "fr"
|
||||
languages = [
|
||||
{ code = "en" },
|
||||
]
|
||||
|
||||
[translations]
|
||||
[translations.fr]
|
||||
|
@ -562,4 +635,62 @@ title = "A title"
|
|||
let error = Trans::new(config).call(&args).unwrap_err();
|
||||
assert_eq!("Failed to retreive term translation", format!("{}", error));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_when_language_not_available() {
|
||||
let config = Config::parse(TRANS_CONFIG).unwrap();
|
||||
let static_fn = GetUrl::new(config, HashMap::new(), TEST_CONTEXT.content_path.clone());
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("@/a_section/a_page.md").unwrap());
|
||||
args.insert("lang".to_string(), to_value("it").unwrap());
|
||||
let err = static_fn.call(&args).unwrap_err();
|
||||
assert_eq!(
|
||||
"`it` is not an authorized language (check config.languages).",
|
||||
format!("{}", err)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_get_url_with_default_language() {
|
||||
let config = Config::parse(TRANS_CONFIG).unwrap();
|
||||
let mut permalinks = HashMap::new();
|
||||
permalinks.insert(
|
||||
"a_section/a_page.md".to_string(),
|
||||
"https://remplace-par-ton-url.fr/a_section/a_page/".to_string(),
|
||||
);
|
||||
permalinks.insert(
|
||||
"a_section/a_page.en.md".to_string(),
|
||||
"https://remplace-par-ton-url.fr/en/a_section/a_page/".to_string(),
|
||||
);
|
||||
let static_fn = GetUrl::new(config, permalinks, TEST_CONTEXT.content_path.clone());
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("@/a_section/a_page.md").unwrap());
|
||||
args.insert("lang".to_string(), to_value("fr").unwrap());
|
||||
assert_eq!(
|
||||
static_fn.call(&args).unwrap(),
|
||||
"https://remplace-par-ton-url.fr/a_section/a_page/"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_get_url_with_other_language() {
|
||||
let config = Config::parse(TRANS_CONFIG).unwrap();
|
||||
let mut permalinks = HashMap::new();
|
||||
permalinks.insert(
|
||||
"a_section/a_page.md".to_string(),
|
||||
"https://remplace-par-ton-url.fr/a_section/a_page/".to_string(),
|
||||
);
|
||||
permalinks.insert(
|
||||
"a_section/a_page.en.md".to_string(),
|
||||
"https://remplace-par-ton-url.fr/en/a_section/a_page/".to_string(),
|
||||
);
|
||||
let static_fn = GetUrl::new(config, permalinks, TEST_CONTEXT.content_path.clone());
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("@/a_section/a_page.md").unwrap());
|
||||
args.insert("lang".to_string(), to_value("en").unwrap());
|
||||
assert_eq!(
|
||||
static_fn.call(&args).unwrap(),
|
||||
"https://remplace-par-ton-url.fr/en/a_section/a_page/"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ lazy_static! {
|
|||
let mut tera = Tera::default();
|
||||
tera.add_raw_templates(vec![
|
||||
("__zola_builtins/404.html", include_str!("builtins/404.html")),
|
||||
("__zola_builtins/atom.xml", include_str!("builtins/atom.xml")),
|
||||
("__zola_builtins/rss.xml", include_str!("builtins/rss.xml")),
|
||||
("__zola_builtins/sitemap.xml", include_str!("builtins/sitemap.xml")),
|
||||
("__zola_builtins/robots.txt", include_str!("builtins/robots.txt")),
|
||||
|
|
|
@ -13,6 +13,7 @@ serde = "1"
|
|||
serde_derive = "1"
|
||||
slug = "0.1"
|
||||
percent-encoding = "2"
|
||||
filetime = "0.2.8"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::fs::{copy, create_dir_all, read_dir, File};
|
||||
use filetime::{set_file_mtime, FileTime};
|
||||
use std::fs::{copy, create_dir_all, metadata, read_dir, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::SystemTime;
|
||||
|
@ -94,7 +95,11 @@ pub fn find_related_assets(path: &Path) -> Vec<PathBuf> {
|
|||
}
|
||||
|
||||
/// Copy a file but takes into account where to start the copy as
|
||||
/// there might be folders we need to create on the way
|
||||
/// there might be folders we need to create on the way.
|
||||
/// No copy occurs if all of the following conditions are satisfied:
|
||||
/// 1. A file with the same name already exists in the dest path.
|
||||
/// 2. Its modification timestamp is identical to that of the src file.
|
||||
/// 3. Its filesize is identical to that of the src file.
|
||||
pub fn copy_file(src: &Path, dest: &PathBuf, base_path: &PathBuf, hard_link: bool) -> Result<()> {
|
||||
let relative_path = src.strip_prefix(base_path).unwrap();
|
||||
let target_path = dest.join(relative_path);
|
||||
|
@ -106,7 +111,19 @@ pub fn copy_file(src: &Path, dest: &PathBuf, base_path: &PathBuf, hard_link: boo
|
|||
if hard_link {
|
||||
std::fs::hard_link(src, target_path)?
|
||||
} else {
|
||||
copy(src, target_path)?;
|
||||
let src_metadata = metadata(src)?;
|
||||
let src_mtime = FileTime::from_last_modification_time(&src_metadata);
|
||||
if Path::new(&target_path).is_file() {
|
||||
let target_metadata = metadata(&target_path)?;
|
||||
let target_mtime = FileTime::from_last_modification_time(&target_metadata);
|
||||
if !(src_mtime == target_mtime && src_metadata.len() == target_metadata.len()) {
|
||||
copy(src, &target_path)?;
|
||||
set_file_mtime(&target_path, src_mtime)?;
|
||||
}
|
||||
} else {
|
||||
copy(src, &target_path)?;
|
||||
set_file_mtime(&target_path, src_mtime)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -160,11 +177,14 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::File;
|
||||
use std::fs::{metadata, read_to_string, File};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use tempfile::tempdir;
|
||||
use tempfile::{tempdir, tempdir_in};
|
||||
|
||||
use super::find_related_assets;
|
||||
use super::{copy_file, find_related_assets};
|
||||
|
||||
#[test]
|
||||
fn can_find_related_assets() {
|
||||
|
@ -181,4 +201,68 @@ mod tests {
|
|||
assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "graph.jpg").count(), 1);
|
||||
assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "fail.png").count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_file_timestamp_preserved() {
|
||||
let base_path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap();
|
||||
let src_dir =
|
||||
tempdir_in(&base_path).expect("failed to create a temporary source directory.");
|
||||
let dest_dir =
|
||||
tempdir_in(&base_path).expect("failed to create a temporary destination directory.");
|
||||
let src_file_path = src_dir.path().join("test.txt");
|
||||
let dest_file_path = dest_dir.path().join(src_file_path.strip_prefix(&base_path).unwrap());
|
||||
File::create(&src_file_path).unwrap();
|
||||
copy_file(&src_file_path, &dest_dir.path().to_path_buf(), &base_path, false).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
metadata(&src_file_path).and_then(|m| m.modified()).unwrap(),
|
||||
metadata(&dest_file_path).and_then(|m| m.modified()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_copy_file_already_exists() {
|
||||
let base_path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap();
|
||||
let src_dir =
|
||||
tempdir_in(&base_path).expect("failed to create a temporary source directory.");
|
||||
let dest_dir =
|
||||
tempdir_in(&base_path).expect("failed to create a temporary destination directory.");
|
||||
let src_file_path = src_dir.path().join("test.txt");
|
||||
let dest_file_path = dest_dir.path().join(src_file_path.strip_prefix(&base_path).unwrap());
|
||||
{
|
||||
let mut src_file = File::create(&src_file_path).unwrap();
|
||||
src_file.write_all(b"file1").unwrap();
|
||||
}
|
||||
copy_file(&src_file_path, &dest_dir.path().to_path_buf(), &base_path, false).unwrap();
|
||||
{
|
||||
let mut dest_file = File::create(&dest_file_path).unwrap();
|
||||
dest_file.write_all(b"file2").unwrap();
|
||||
}
|
||||
|
||||
// Check copy does not occur when moditication timestamps and filesizes are same.
|
||||
filetime::set_file_mtime(&src_file_path, filetime::FileTime::from_unix_time(0, 0)).unwrap();
|
||||
filetime::set_file_mtime(&dest_file_path, filetime::FileTime::from_unix_time(0, 0))
|
||||
.unwrap();
|
||||
copy_file(&src_file_path, &dest_dir.path().to_path_buf(), &base_path, false).unwrap();
|
||||
assert_eq!(read_to_string(&src_file_path).unwrap(), "file1");
|
||||
assert_eq!(read_to_string(&dest_file_path).unwrap(), "file2");
|
||||
|
||||
// Copy occurs if the timestamps are different while the filesizes are same.
|
||||
filetime::set_file_mtime(&dest_file_path, filetime::FileTime::from_unix_time(42, 42))
|
||||
.unwrap();
|
||||
copy_file(&src_file_path, &dest_dir.path().to_path_buf(), &base_path, false).unwrap();
|
||||
assert_eq!(read_to_string(&src_file_path).unwrap(), "file1");
|
||||
assert_eq!(read_to_string(&dest_file_path).unwrap(), "file1");
|
||||
|
||||
// Copy occurs if the timestamps are same while the filesizes are different.
|
||||
{
|
||||
let mut dest_file = File::create(&dest_file_path).unwrap();
|
||||
dest_file.write_all(b"This file has different file size to the source file!").unwrap();
|
||||
}
|
||||
filetime::set_file_mtime(&dest_file_path, filetime::FileTime::from_unix_time(0, 0))
|
||||
.unwrap();
|
||||
copy_file(&src_file_path, &dest_dir.path().to_path_buf(), &base_path, false).unwrap();
|
||||
assert_eq!(read_to_string(&src_file_path).unwrap(), "file1");
|
||||
assert_eq!(read_to_string(&dest_file_path).unwrap(), "file1");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ weight = 50
|
|||
|
||||
## Heading id and anchor insertion
|
||||
While rendering the Markdown content, a unique id will automatically be assigned to each heading.
|
||||
This id is created by converting the heading text to a [slug](https://en.wikipedia.org/wiki/Semantic_URL#Slug) if `slugify_paths` is enabled.
|
||||
if `slugify_paths` is disabled, whitespaces are replaced by `_` and the following characters are stripped: `#`, `%`, `<`, `>`, `[`, `]`, `(`, `)`, \`, `^`, `{`, `|`, `}`.
|
||||
This id is created by converting the heading text to a [slug](https://en.wikipedia.org/wiki/Semantic_URL#Slug) if `slugify.anchors` is set to `"on"` (the default).
|
||||
If `slugify.paths` is set to `"safe"`, whitespaces are replaced by `_` and the following characters are stripped: `#`, `%`, `<`, `>`, `[`, `]`, `(`, `)`, \`, `^`, `{`, `|`, `}`.
|
||||
If `slugify.paths` is set to `"off"`, no modifications are made, and you may be left with nominally illegal ids.
|
||||
A number is appended at the end if the slug already exists for that article
|
||||
For example:
|
||||
|
||||
|
@ -38,7 +39,12 @@ This option is set at the section level: the `insert_anchor_links` variable on t
|
|||
|
||||
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, it can be easily overwritten by
|
||||
creating an `anchor-link.html` file in the `templates` directory, which gets an `id` variable.
|
||||
creating an `anchor-link.html` file in the `templates` directory.
|
||||
|
||||
The anchor link template has the following variables:
|
||||
|
||||
- `id`: the heading's id after applying the rules defined by `slugify.anchors`
|
||||
- `level`: the heading level (between 1 and 6)
|
||||
|
||||
## Internal links
|
||||
Linking to other pages and their headings is so common that Zola adds a
|
||||
|
|
|
@ -11,9 +11,9 @@ to your `config.toml`. For example:
|
|||
|
||||
```toml
|
||||
languages = [
|
||||
{code = "fr", rss = true}, # there will be a RSS feed for French content
|
||||
{code = "fr", feed = true}, # there will be a feed for French content
|
||||
{code = "fr", search = true}, # there will be a Search Index for French content
|
||||
{code = "it"}, # there won't be a RSS feed for Italian content
|
||||
{code = "it"}, # there won't be a feed for Italian content
|
||||
]
|
||||
```
|
||||
|
||||
|
|
|
@ -35,12 +35,13 @@ For any page within your content folder, its output path will be defined by eith
|
|||
- its filename
|
||||
|
||||
Either way, these proposed path will be sanitized before being used.
|
||||
If `slugify_paths` is enabled in the site's config - the default - paths are [slugified](https://en.wikipedia.org/wiki/Clean_URL#Slug).
|
||||
Otherwise, a simpler sanitation is performed, outputting only valid NTFS paths.
|
||||
The following characters are removed: `<`, `>`, `:`, `/`, `|`, `?`, `*`, `#`, `\\`, `(`, `)`, `[`, `]` as well as newlines and tabulations.
|
||||
If `slugify.paths` is set to `"on"` in the site's config - the default - paths are [slugified](https://en.wikipedia.org/wiki/Clean_URL#Slug).
|
||||
If it is set to `"safe"`, only sanitation is performed, with the following characters being removed: `<`, `>`, `:`, `/`, `|`, `?`, `*`, `#`, `\\`, `(`, `)`, `[`, `]` as well as newlines and tabulations. This ensures that the path can be represented on all operating systems.
|
||||
Additionally, trailing whitespace and dots are removed and whitespaces are replaced by `_`.
|
||||
|
||||
**NOTE:** To produce URLs containing non-English characters (UTF8), `slugify_paths` needs to be set to `false`.
|
||||
If `slugify.paths` is set to `"off"`, no modifications are made.
|
||||
|
||||
If you want URLs containing non-ASCII characters, `slugify.paths` needs to be set to `"safe"` or `"off"`.
|
||||
|
||||
### Path from frontmatter
|
||||
|
||||
|
@ -56,7 +57,7 @@ slug = "femmes-libres-libération-kurde"
|
|||
This is my article.
|
||||
```
|
||||
|
||||
This frontmatter will output the article to `[base_url]/zines/femmes-libres-libération-kurde` with `slugify_paths` disabled, and to `[base_url]/zines/femmes-libres-liberation-kurde` with `slugify_enabled` enabled.
|
||||
This frontmatter will output the article to `[base_url]/zines/femmes-libres-libération-kurde` with `slugify.paths` set to `"safe"` or `"off"`, and to `[base_url]/zines/femmes-libres-liberation-kurde` with the default value for `slugify.paths` of `"on"`.
|
||||
|
||||
### Path from filename
|
||||
|
||||
|
@ -66,7 +67,7 @@ When the article's output path is not specified in the frontmatter, it is extrac
|
|||
|
||||
If the path found starts with a datetime string (`YYYY-mm-dd` or [a RFC3339 datetime](https://www.ietf.org/rfc/rfc3339.txt)) followed by an underscore (`_`) or a dash (`-`), this date is removed from the output path and will be used as the page date (unless already set in the front-matter). Note that the full RFC3339 datetime contains colons, which is not a valid character in a filename on Windows.
|
||||
|
||||
The output path extracted from the file path is then slugified or not depending on the `slugify_paths` config, as explained previously.
|
||||
The output path extracted from the file path is then slugified or not, depending on the `slugify.paths` config, as explained previously.
|
||||
|
||||
**Example:**
|
||||
The file `content/blog/2018-10-10-hello-world.md` will yield a page at `[base_url]/blog/hello-world`.
|
||||
|
@ -93,6 +94,10 @@ description = ""
|
|||
# Setting this overrides a date set in the filename.
|
||||
date =
|
||||
|
||||
# The last updated date of the post, if different from the date.
|
||||
# Same format as `date`.
|
||||
updated =
|
||||
|
||||
# The weight as defined on the Section page of the documentation.
|
||||
# If the section variable `sort_by` is set to `weight`, then any page that lacks a `weight`
|
||||
# will not be rendered.
|
||||
|
|
|
@ -82,7 +82,7 @@ render = true
|
|||
# Useful for the same reason as `render` but when you don't want a 404 when
|
||||
# landing on the root section page.
|
||||
# Example: redirect_to = "documentation/content/overview"
|
||||
redirect_to = ""
|
||||
redirect_to =
|
||||
|
||||
# If set to "true", the section will pass its pages on to the parent section. Defaults to `false`.
|
||||
# Useful when the section shouldn't split up the parent section, like
|
||||
|
|
|
@ -24,16 +24,18 @@ Here is a full list of supported languages and their short names:
|
|||
|
||||
```
|
||||
- ActionScript -> ["as"]
|
||||
- Advanced CSV -> ["csv", "tsv"]
|
||||
- AppleScript -> ["applescript", "script editor"]
|
||||
- ASP -> ["asa"]
|
||||
- Assembly x86 (NASM) -> ["asm", "inc", "nasm"]
|
||||
- AWK -> ["awk"]
|
||||
- Batch File -> ["bat", "cmd"]
|
||||
- BibTeX -> ["bib"]
|
||||
- Bourne Again Shell (bash) -> [".bash_aliases", ".bash_completions", ".bash_functions", ".bash_login", ".bash_logout", ".bash_profile", ".bash_variables", ".bashrc", ".profile", ".textmate_init", ".zshrc", "bash", "fish", "sh", "zsh"]
|
||||
- Bourne Again Shell (bash) -> [".bash_aliases", ".bash_completions", ".bash_functions", ".bash_login", ".bash_logout", ".bash_profile", ".bash_variables", ".bashrc", ".ebuild", ".eclass", ".profile", ".textmate_init", ".zlogin", ".zlogout", ".zprofile", ".zshenv", ".zshrc", "PKGBUILD", "ash", "bash", "sh", "zsh"]
|
||||
- C -> ["c", "h"]
|
||||
- C# -> ["cs", "csx"]
|
||||
- C++ -> ["C", "c++", "cc", "cp", "cpp", "cxx", "h", "h++", "hh", "hpp", "hxx", "inl", "ipp"]
|
||||
- Clojure -> ["clj"]
|
||||
- Clojure -> ["clj", "cljc", "cljs", "edn"]
|
||||
- CMake -> ["CMakeLists.txt", "cmake"]
|
||||
- CMake C Header -> ["h.in"]
|
||||
- CMake C++ Header -> ["h++.in", "hh.in", "hpp.in", "hxx.in"]
|
||||
|
@ -43,10 +45,15 @@ Here is a full list of supported languages and their short names:
|
|||
- D -> ["d", "di"]
|
||||
- Dart -> ["dart"]
|
||||
- Diff -> ["diff", "patch"]
|
||||
- Dockerfile -> ["Dockerfile", "dockerfile"]
|
||||
- Elixir -> ["ex", "exs"]
|
||||
- Elm -> ["elm"]
|
||||
- Erlang -> ["Emakefile", "emakefile", "erl", "hrl"]
|
||||
- fsharp -> ["fs"]
|
||||
- Erlang -> ["Emakefile", "emakefile", "erl", "escript", "hrl"]
|
||||
- F# -> ["fs", "fsi", "fsx"]
|
||||
- Fortran (Fixed Form) -> ["F", "F77", "FOR", "FPP", "f", "f77", "for", "fpp"]
|
||||
- Fortran (Modern) -> ["F03", "F08", "F90", "F95", "f03", "f08", "f90", "f95"]
|
||||
- Fortran Namelist -> ["namelist"]
|
||||
- Friendly Interactive Shell (fish) -> ["fish"]
|
||||
- Generic Config -> [".dircolors", ".gitattributes", ".gitignore", ".gitmodules", ".inputrc", "Doxyfile", "cfg", "conf", "config", "dircolors", "gitattributes", "gitignore", "gitmodules", "ini", "inputrc", "mak", "mk", "pro"]
|
||||
- Git Attributes -> [".gitattributes", "attributes", "gitattributes"]
|
||||
- Git Commit -> ["COMMIT_EDITMSG", "MERGE_MSG", "TAG_EDITMSG"]
|
||||
|
@ -54,15 +61,19 @@ Here is a full list of supported languages and their short names:
|
|||
- Git Ignore -> [".gitignore", "exclude", "gitignore"]
|
||||
- Git Link -> [".git"]
|
||||
- Git Log -> ["gitlog"]
|
||||
- Git Mailmap -> [".mailmap", "mailmap"]
|
||||
- Git Rebase Todo -> ["git-rebase-todo"]
|
||||
- Go -> ["go"]
|
||||
- GraphQL -> ["gql", "graphql"]
|
||||
- Graphviz (DOT) -> ["DOT", "dot", "gv"]
|
||||
- Groovy -> ["Jenkinsfile", "gradle", "groovy", "gvy"]
|
||||
- Handlebars -> ["handlebars", "handlebars.html", "hbr", "hbrs", "hbs", "hdbs", "hjs", "mu", "mustache", "rac", "stache", "template", "tmpl"]
|
||||
- Haskell -> ["hs"]
|
||||
- HTML -> ["htm", "html", "shtml", "xhtml"]
|
||||
- HTML (ASP) -> ["asp"]
|
||||
- HTML (EEx) -> ["html.eex", "html.leex"]
|
||||
- HTML (Erlang) -> ["yaws"]
|
||||
- HTML (Jinja2) -> ["htm.j2", "html.j2", "xhtml.j2", "xml.j2"]
|
||||
- HTML (Rails) -> ["erb", "html.erb", "rails", "rhtml"]
|
||||
- HTML (Tcl) -> ["adp"]
|
||||
- Java -> ["bsh", "java"]
|
||||
|
@ -70,8 +81,8 @@ Here is a full list of supported languages and their short names:
|
|||
- Java Server Page (JSP) -> ["jsp"]
|
||||
- JavaScript -> ["htc", "js"]
|
||||
- JavaScript (Rails) -> ["js.erb"]
|
||||
- Jinja2 -> ["j2", "jinja2"]
|
||||
- JSON -> ["json", "sublime-build", "sublime-color-scheme", "sublime-commands", "sublime-completions", "sublime-keymap", "sublime-macro", "sublime-menu", "sublime-mousemap", "sublime-project", "sublime-settings", "sublime-theme"]
|
||||
- Jinja2 -> ["j2", "jinja", "jinja2"]
|
||||
- JSON -> ["Pipfile.lock", "ipynb", "json", "sublime-build", "sublime-color-scheme", "sublime-commands", "sublime-completions", "sublime-keymap", "sublime-macro", "sublime-menu", "sublime-mousemap", "sublime-project", "sublime-settings", "sublime-theme"]
|
||||
- Julia -> ["jl"]
|
||||
- Kotlin -> ["kt", "kts"]
|
||||
- LaTeX -> ["ltx", "tex"]
|
||||
|
@ -94,24 +105,30 @@ Here is a full list of supported languages and their short names:
|
|||
- OCamllex -> ["mll"]
|
||||
- OCamlyacc -> ["mly"]
|
||||
- Pascal -> ["dpr", "p", "pas"]
|
||||
- Perl -> ["PL", "pl", "pm", "pod", "t"]
|
||||
- Perl -> ["pc", "pl", "pm", "pmc", "pod", "t"]
|
||||
- PHP -> ["php", "php3", "php4", "php5", "php7", "phps", "phpt", "phtml"]
|
||||
- Plain Text -> ["txt"]
|
||||
- PowerShell -> ["ps1", "psd1", "psm1"]
|
||||
- Python -> ["SConscript", "SConstruct", "Sconstruct", "Snakefile", "cpy", "gyp", "gypi", "pxd", "pxd.in", "pxi", "pxi.in", "py", "py3", "pyi", "pyw", "pyx", "pyx.in", "rpy", "sconstruct", "wscript"]
|
||||
- R -> ["R", "Rprofile", "S", "r", "s"]
|
||||
- PureScript -> ["purs"]
|
||||
- Python -> ["SConscript", "SConstruct", "Sconstruct", "Snakefile", "bazel", "bzl", "cpy", "gyp", "gypi", "pxd", "pxd.in", "pxi", "pxi.in", "py", "py3", "pyi", "pyw", "pyx", "pyx.in", "rpy", "sconstruct", "vpy", "wscript"]
|
||||
- R -> ["R", "Rprofile", "r"]
|
||||
- Racket -> ["rkt"]
|
||||
- Rd (R Documentation) -> ["rd"]
|
||||
- Reason -> ["re", "rei"]
|
||||
- Regular Expression -> ["re"]
|
||||
- Regular Expressions (Elixir) -> ["ex.re"]
|
||||
- reStructuredText -> ["rest", "rst"]
|
||||
- Ruby -> ["Appfile", "Appraisals", "Berksfile", "Brewfile", "Cheffile", "Deliverfile", "Fastfile", "Gemfile", "Guardfile", "Rakefile", "Rantfile", "Scanfile", "Snapfile", "Thorfile", "Vagrantfile", "capfile", "cgi", "config.ru", "fcgi", "gemspec", "irbrc", "jbuilder", "podspec", "prawn", "rabl", "rake", "rb", "rbx", "rjs", "ruby.rail", "simplecov", "thor"]
|
||||
- Ruby -> ["Appfile", "Appraisals", "Berksfile", "Brewfile", "Cheffile", "Deliverfile", "Fastfile", "Gemfile", "Guardfile", "Podfile", "Rakefile", "Rantfile", "Scanfile", "Snapfile", "Thorfile", "Vagrantfile", "capfile", "cgi", "config.ru", "fcgi", "gemspec", "irbrc", "jbuilder", "podspec", "prawn", "rabl", "rake", "rb", "rbx", "rjs", "ruby.rail", "simplecov", "thor"]
|
||||
- Ruby Haml -> ["haml", "sass"]
|
||||
- Ruby on Rails -> ["builder", "rxml"]
|
||||
- Rust -> ["rs"]
|
||||
- Scala -> ["sbt", "scala"]
|
||||
- Sass -> ["sass"]
|
||||
- Scala -> ["sbt", "sc", "scala"]
|
||||
- SCSS -> ["scss"]
|
||||
- SQL -> ["ddl", "dml", "sql"]
|
||||
- SQL (Rails) -> ["erbsql", "sql.erb"]
|
||||
- srt -> ["srt", "subrip"]
|
||||
- Stylus -> ["styl", "stylus"]
|
||||
- SWI-Prolog -> ["pro"]
|
||||
- Swift -> ["swift"]
|
||||
- Tcl -> ["tcl"]
|
||||
|
@ -121,7 +138,7 @@ Here is a full list of supported languages and their short names:
|
|||
- TypeScript -> ["ts"]
|
||||
- TypeScriptReact -> ["tsx"]
|
||||
- VimL -> ["vim"]
|
||||
- XML -> ["dtml", "opml", "rss", "svg", "tld", "xml", "xsd", "xslt"]
|
||||
- XML -> ["dtml", "opml", "rng", "rss", "svg", "tld", "xml", "xsd", "xslt"]
|
||||
- YAML -> ["sublime-syntax", "yaml", "yml"]
|
||||
```
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ A taxonomy has five variables:
|
|||
- `paginate_by`: if this is set to a number, each term page will be paginated by this much.
|
||||
- `paginate_path`: if set, this path will be used by the paginated page and the page number will be appended after it.
|
||||
For example the default would be page/1.
|
||||
- `rss`: if set to `true`, an RSS feed will be generated for each term.
|
||||
- `feed`: if set to `true`, a feed (atom by default) will be generated for each term.
|
||||
- `lang`: only set this if you are making a multilingual site and want to indicate which language this taxonomy is for
|
||||
|
||||
**Example 1:** (one language)
|
||||
|
@ -52,7 +52,7 @@ categories = ["programming"]
|
|||
|
||||
In a similar manner to how section and pages calculate their output path:
|
||||
- the taxonomy name is never slugified
|
||||
- the taxonomy entry (eg. as specific tag) is slugified when `slugify_paths` is enabled in the configuration
|
||||
- the taxonomy term (eg. as specific tag) is slugified when `slugify.taxonomies` is enabled (`"on"`, the default) in the configuration
|
||||
|
||||
The taxonomy pages are then available at the following paths:
|
||||
|
||||
|
|
|
@ -17,11 +17,11 @@ used by Zola as well as their default values are listed below:
|
|||
# The base URL of the site; the only required configuration variable.
|
||||
base_url = "mywebsite.com"
|
||||
|
||||
# The site title and description; used in RSS by default.
|
||||
# The site title and description; used in feeds by default.
|
||||
title = ""
|
||||
description = ""
|
||||
|
||||
# The default language; used in RSS.
|
||||
# The default language; used in feeds.
|
||||
default_language = "en"
|
||||
|
||||
# The site theme to use.
|
||||
|
@ -34,12 +34,17 @@ highlight_code = false
|
|||
# See below for list of allowed values.
|
||||
highlight_theme = "base16-ocean-dark"
|
||||
|
||||
# When set to "true", an RSS feed is automatically generated.
|
||||
generate_rss = false
|
||||
# When set to "true", a feed is automatically generated.
|
||||
generate_feed = false
|
||||
|
||||
# The number of articles to include in the RSS feed. All items are included if
|
||||
# The filename to use for the feed. Used as the template filename, too.
|
||||
# Defaults to "atom.xml", which has a builtin template that renders an Atom 1.0 feed.
|
||||
# There is also a builtin template "rss.xml" that renders an RSS 2.0 feed.
|
||||
# feed_filename = "atom.xml"
|
||||
|
||||
# The number of articles to include in the feed. All items are included if
|
||||
# this limit is not set (the default).
|
||||
# rss_limit = 20
|
||||
# feed_limit = 20
|
||||
|
||||
# When set to "true", files in the `static` directory are hard-linked. Useful for large
|
||||
# static files. Note that for this to work, both `static` and the
|
||||
|
@ -50,10 +55,10 @@ generate_rss = false
|
|||
# The taxonomies to be rendered for the site and their configuration.
|
||||
# Example:
|
||||
# taxonomies = [
|
||||
# {name = "tags", rss = true}, # each tag will have its own RSS feed
|
||||
# {name = "tags", feed = true}, # each tag will have its own feed
|
||||
# {name = "tags", lang = "fr"}, # you can have taxonomies with the same name in multiple languages
|
||||
# {name = "categories", paginate_by = 5}, # 5 items per page for a term
|
||||
# {name = "authors"}, # Basic definition: no RSS or pagination
|
||||
# {name = "authors"}, # Basic definition: no feed or pagination
|
||||
# ]
|
||||
#
|
||||
taxonomies = []
|
||||
|
@ -61,9 +66,9 @@ taxonomies = []
|
|||
# The additional languages for the site.
|
||||
# Example:
|
||||
# languages = [
|
||||
# {code = "fr", rss = true}, # there will be a RSS feed for French content
|
||||
# {code = "fr", feed = true}, # there will be a feed for French content
|
||||
# {code = "fr", search = true}, # there will be a Search Index for French content
|
||||
# {code = "it"}, # there won't be a RSS feed for Italian content
|
||||
# {code = "it"}, # there won't be a feed for Italian content
|
||||
# ]
|
||||
#
|
||||
languages = []
|
||||
|
@ -155,6 +160,7 @@ Zola currently has the following highlight themes available:
|
|||
- [material-dark](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Material%20Dark)
|
||||
- [material-light](https://github.com/morhetz/gruvbox)
|
||||
- [monokai](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Monokai)
|
||||
- [nord](https://github.com/crabique/Nord-plist/tree/0d655b23d6b300e691676d9b90a68d92b267f7ec)
|
||||
- [nyx-bold](https://github.com/GalAster/vscode-theme-nyx)
|
||||
- [one-dark](https://github.com/andresmichel/one-dark-theme)
|
||||
- [solarized-dark](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Solarized%20(dark))
|
||||
|
@ -162,6 +168,7 @@ Zola currently has the following highlight themes available:
|
|||
- [subway-madrid](https://github.com/idleberg/Subway.tmTheme)
|
||||
- [subway-moscow](https://github.com/idleberg/Subway.tmTheme)
|
||||
- [Tomorrow](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Tomorrow)
|
||||
- [TwoDark](https://github.com/erremauro/TwoDark)
|
||||
- [visual-studio-dark](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Visual%20Studio%20Dark)
|
||||
- [zenburn](https://github.com/colinta/zenburn)
|
||||
|
||||
|
|
34
docs/content/documentation/templates/feeds.md
Normal file
34
docs/content/documentation/templates/feeds.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
+++
|
||||
title = "Feeds"
|
||||
weight = 50
|
||||
aliases = ["/documentation/templates/rss/"]
|
||||
+++
|
||||
|
||||
If the site `config.toml` file sets `generate_feed = true`, then Zola will
|
||||
generate a feed file for the site, named according to the `feed_filename`
|
||||
setting in `config.toml`, which defaults to `atom.xml`. Given the feed filename
|
||||
`atom.xml`, the generated file will live at `base_url/atom.xml`, based upon the
|
||||
`atom.xml` file in the `templates` directory, or the built-in Atom template.
|
||||
|
||||
`feed_filename` can be set to any value, but built-in templates are provided
|
||||
for `atom.xml` (in the preferred Atom 1.0 format), and `rss.xml` (in the RSS
|
||||
2.0 format). If you choose a different filename (e.g. `feed.xml`), you will
|
||||
need to provide a template yourself.
|
||||
|
||||
**Only pages with a date will be available.**
|
||||
|
||||
The feed template gets five variables:
|
||||
|
||||
- `config`: the site config
|
||||
- `feed_url`: the full url to that specific feed
|
||||
- `last_updated`: the most recent `updated` or `date` field of any post
|
||||
- `pages`: see [page variables](@/documentation/templates/pages-sections.md#page-variables)
|
||||
for a detailed description of what this contains
|
||||
- `lang`: the language code that applies to all of the pages in the feed,
|
||||
if the site is multilingual, or `config.default_language` if it is not
|
||||
|
||||
Feeds for taxonomy terms get two more variables, using types from the
|
||||
[taxonomies templates](@/documentation/templates/taxonomies.md):
|
||||
|
||||
- `taxonomy`: of type `TaxonomyConfig`
|
||||
- `term`: of type `TaxonomyTerm`, but without `term.pages` (use `pages` instead)
|
|
@ -13,13 +13,14 @@ to learn more about it first.
|
|||
All templates live in the `templates` directory. If you are not sure what variables are available in a template,
|
||||
you can place `{{ __tera_context }}` in the template to print the whole context.
|
||||
|
||||
A few variables are available on all templates except RSS and the sitemap:
|
||||
A few variables are available on all templates except feeds and the sitemap:
|
||||
|
||||
- `config`: the [configuration](@/documentation/getting-started/configuration.md) without any modifications
|
||||
- `current_path`: the path (full URL without `base_url`) of the current page, never starting with a `/`
|
||||
- `current_url`: the full URL for the current page
|
||||
- `lang`: the language for the current page; `null` if the page/section doesn't have a language set
|
||||
- `lang`: the language for the current page
|
||||
|
||||
Config variables can be accessed like `config.variable`, in HTML for example with `{{ config.base_url }}`.
|
||||
The 404 template does not get `current_path` and `current_url` (this information cannot be determined).
|
||||
|
||||
## Standard templates
|
||||
|
@ -35,12 +36,13 @@ section variables. The `page.html` template has access to the page variables.
|
|||
The page and section variables are described in more detail in the next section.
|
||||
|
||||
## Built-in templates
|
||||
Zola comes with three built-in templates: `rss.xml`, `sitemap.xml` and
|
||||
`robots.txt` (each is described in its own section of this documentation).
|
||||
Zola comes with four built-in templates: `atom.xml` and `rss.xml` (described in
|
||||
[Feeds](@/documentation/templates/feeds.md)), `sitemap.xml` (described in [Sitemap](@/documentation/templates/sitemap.md)),
|
||||
and `robots.txt` (described in [Robots.txt](@/documentation/templates/robots.md)).
|
||||
Additionally, themes can add their own templates, which will be applied if not
|
||||
overridden. You can override built-in or theme templates by creating a template with
|
||||
the same name in the correct path. For example, you can override the RSS template by
|
||||
creating a `templates/rss.xml` file.
|
||||
the same name in the correct path. For example, you can override the Atom template by
|
||||
creating a `templates/atom.xml` file.
|
||||
|
||||
## Custom templates
|
||||
In addition to the standard `index.html`, `section.html` and `page.html` templates,
|
||||
|
@ -72,6 +74,8 @@ pass `true` to the inline argument:
|
|||
{{ some_text | markdown(inline=true) }}
|
||||
```
|
||||
|
||||
You do not need to use this filter with `page.content` or `section.content`, the content is already rendered.
|
||||
|
||||
### base64_encode
|
||||
Encode the variable to base64.
|
||||
|
||||
|
@ -114,6 +118,16 @@ link like the ones used in Markdown, starting from the root `content` directory.
|
|||
{% set url = get_url(path="@/blog/_index.md") %}
|
||||
```
|
||||
|
||||
It accepts an optionnal parameter `lang` in order to compute a *language-aware URL* in multilingual websites. Assuming `config.base_url` is `"http://example.com"`, the following snippet will:
|
||||
|
||||
- return `"http://example.com/blog/"` if `config.default_language` is `"en"`
|
||||
- return `"http://example.com/en/blog/"` if `config.default_language` is **not** `"en"` and `"en"` appears in `config.languages`
|
||||
- fail otherwise, with the error message `"'en' is not an authorized language (check config.languages)."`
|
||||
|
||||
```jinja2
|
||||
{% set url = get_url(path="@/blog/_index.md", lang="en") %}
|
||||
```
|
||||
|
||||
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`:
|
||||
|
||||
|
@ -128,7 +142,7 @@ An example is:
|
|||
{{/* get_url(path="css/app.css", trailing_slash=true) */}}
|
||||
```
|
||||
|
||||
In the case of non-internal links, you can also add a cachebust of the format `?t=1290192` at the end of a URL
|
||||
In the case of non-internal links, you can also add a cachebust of the format `?h=<sha256>` at the end of a URL
|
||||
by passing `cachebust=true` to the `get_url` function.
|
||||
|
||||
|
||||
|
|
|
@ -14,10 +14,13 @@ with the following fields:
|
|||
|
||||
|
||||
```ts
|
||||
// The HTML output of the Markdown content
|
||||
content: String;
|
||||
title: String?;
|
||||
description: String?;
|
||||
date: String?;
|
||||
// `updated` will be the same as `date` if `date` is specified but `updated` is not in front-matter
|
||||
updated: String?;
|
||||
slug: String;
|
||||
path: String;
|
||||
draft: Bool;
|
||||
|
@ -68,6 +71,7 @@ with the following fields:
|
|||
|
||||
|
||||
```ts
|
||||
// The HTML output of the Markdown content
|
||||
content: String;
|
||||
title: String?;
|
||||
description: String?;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
+++
|
||||
title = "RSS"
|
||||
weight = 50
|
||||
+++
|
||||
|
||||
If the site `config.toml` file sets `generate_rss = true`, then Zola will
|
||||
generate an `rss.xml` page for the site, which will live at `base_url/rss.xml`. To
|
||||
generate the `rss.xml` page, Zola will look for an `rss.xml` file in the `templates`
|
||||
directory or, if one does not exist, it will use the use the built-in rss template.
|
||||
|
||||
**Only pages with a date will be available.**
|
||||
|
||||
The RSS template gets three variables in addition to `config`:
|
||||
|
||||
- `feed_url`: the full url to that specific feed
|
||||
- `last_build_date`: the date of the latest post
|
||||
- `pages`: see [page variables](@/documentation/templates/pages-sections.md#page-variables) for
|
||||
a detailed description of what this contains
|
|
@ -25,7 +25,7 @@ A `SitemapEntry` has the following fields:
|
|||
|
||||
```ts
|
||||
permalink: String;
|
||||
date: String?;
|
||||
updated: String?;
|
||||
extra: Hashmap<String, Any>?;
|
||||
```
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@ and `TaxonomyConfig` has the following fields:
|
|||
|
||||
```ts
|
||||
name: String,
|
||||
slug: String,
|
||||
paginate_by: Number?;
|
||||
paginate_path: String?;
|
||||
rss: Bool;
|
||||
feed: Bool;
|
||||
lang: String;
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -100,8 +100,8 @@ Zulma has 3 taxonomies already set internally: `tags`, `cateogories` and `author
|
|||
```toml
|
||||
taxonomies = [
|
||||
{name = "categories"},
|
||||
{name = "tags", paginate_by = 5, rss = true},
|
||||
{name = "authors", rss = true},
|
||||
{name = "tags", paginate_by = 5, feed = true},
|
||||
{name = "authors", feed = true},
|
||||
]
|
||||
```
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@ The theme requires tags and categories taxonomies to be enabled in your `config.
|
|||
|
||||
```toml
|
||||
taxonomies = [
|
||||
# You can enable/disable RSS
|
||||
{name = "categories", rss = true},
|
||||
{name = "tags", rss = true},
|
||||
# You can enable/disable feeds
|
||||
{name = "categories", feed = true},
|
||||
{name = "tags", feed = true},
|
||||
]
|
||||
```
|
||||
If you want to paginate taxonomies pages, you will need to overwrite the templates
|
||||
|
|
|
@ -48,9 +48,9 @@ The theme requires tags and categories taxonomies to be enabled in your `config.
|
|||
|
||||
```toml
|
||||
taxonomies = [
|
||||
# You can enable/disable RSS
|
||||
{name = "categories", rss = true},
|
||||
{name = "tags", rss = true},
|
||||
# You can enable/disable feeds
|
||||
{name = "categories", feed = true},
|
||||
{name = "tags", feed = true},
|
||||
]
|
||||
```
|
||||
If you want to paginate taxonomies pages, you will need to overwrite the templates
|
||||
|
|
|
@ -18,9 +18,8 @@ pub fn build_cli() -> App<'static, 'static> {
|
|||
Arg::with_name("config")
|
||||
.short("c")
|
||||
.long("config")
|
||||
.default_value("config.toml")
|
||||
.takes_value(true)
|
||||
.help("Path to a config file other than config.toml")
|
||||
.help("Path to a config file other than config.toml in the root of project")
|
||||
)
|
||||
.subcommands(vec![
|
||||
SubCommand::with_name("init")
|
||||
|
|
|
@ -7,9 +7,9 @@ use crate::console;
|
|||
|
||||
pub fn build(
|
||||
root_dir: &Path,
|
||||
config_file: &str,
|
||||
config_file: &Path,
|
||||
base_url: Option<&str>,
|
||||
output_dir: &str,
|
||||
output_dir: &Path,
|
||||
include_drafts: bool,
|
||||
) -> Result<()> {
|
||||
let mut site = Site::new(root_dir, config_file)?;
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::console;
|
|||
|
||||
pub fn check(
|
||||
root_dir: &Path,
|
||||
config_file: &str,
|
||||
config_file: &Path,
|
||||
base_path: Option<&str>,
|
||||
base_url: Option<&str>,
|
||||
include_drafts: bool,
|
||||
|
|
|
@ -161,9 +161,9 @@ fn create_new_site(
|
|||
root_dir: &Path,
|
||||
interface: &str,
|
||||
port: u16,
|
||||
output_dir: &str,
|
||||
output_dir: &Path,
|
||||
base_url: &str,
|
||||
config_file: &str,
|
||||
config_file: &Path,
|
||||
include_drafts: bool,
|
||||
) -> Result<(Site, String)> {
|
||||
let mut site = Site::new(root_dir, config_file)?;
|
||||
|
@ -194,9 +194,9 @@ pub fn serve(
|
|||
root_dir: &Path,
|
||||
interface: &str,
|
||||
port: u16,
|
||||
output_dir: &str,
|
||||
output_dir: &Path,
|
||||
base_url: &str,
|
||||
config_file: &str,
|
||||
config_file: &Path,
|
||||
watch_only: bool,
|
||||
open: bool,
|
||||
include_drafts: bool,
|
||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -16,7 +16,10 @@ fn main() {
|
|||
"." => env::current_dir().unwrap(),
|
||||
path => PathBuf::from(path),
|
||||
};
|
||||
let config_file = matches.value_of("config").unwrap();
|
||||
let config_file = match matches.value_of("config") {
|
||||
Some(path) => PathBuf::from(path),
|
||||
None => root_dir.join("config.toml"),
|
||||
};
|
||||
|
||||
match matches.subcommand() {
|
||||
("init", Some(matches)) => {
|
||||
|
@ -31,12 +34,12 @@ fn main() {
|
|||
("build", Some(matches)) => {
|
||||
console::info("Building site...");
|
||||
let start = Instant::now();
|
||||
let output_dir = matches.value_of("output_dir").unwrap();
|
||||
let output_dir = PathBuf::from(matches.value_of("output_dir").unwrap());
|
||||
match cmd::build(
|
||||
&root_dir,
|
||||
config_file,
|
||||
&config_file,
|
||||
matches.value_of("base_url"),
|
||||
output_dir,
|
||||
&output_dir,
|
||||
matches.is_present("drafts"),
|
||||
) {
|
||||
Ok(()) => console::report_elapsed_time(start),
|
||||
|
@ -73,16 +76,16 @@ fn main() {
|
|||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
let output_dir = matches.value_of("output_dir").unwrap();
|
||||
let output_dir = PathBuf::from(matches.value_of("output_dir").unwrap());
|
||||
let base_url = matches.value_of("base_url").unwrap();
|
||||
console::info("Building site...");
|
||||
match cmd::serve(
|
||||
&root_dir,
|
||||
interface,
|
||||
port,
|
||||
output_dir,
|
||||
&output_dir,
|
||||
base_url,
|
||||
config_file,
|
||||
&config_file,
|
||||
watch_only,
|
||||
open,
|
||||
include_drafts,
|
||||
|
@ -99,7 +102,7 @@ fn main() {
|
|||
let start = Instant::now();
|
||||
match cmd::check(
|
||||
&root_dir,
|
||||
config_file,
|
||||
&config_file,
|
||||
matches.value_of("base_path"),
|
||||
matches.value_of("base_url"),
|
||||
matches.is_present("drafts"),
|
||||
|
|
46
sublime/syntaxes/CSV.sublime-syntax
Normal file
46
sublime/syntaxes/CSV.sublime-syntax
Normal file
|
@ -0,0 +1,46 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Advanced CSV
|
||||
file_extensions:
|
||||
- csv
|
||||
- tsv
|
||||
scope: text.advanced_csv
|
||||
contexts:
|
||||
main:
|
||||
- match: (\")
|
||||
captures:
|
||||
1: string.quoted.double.advanced_csv
|
||||
push:
|
||||
- meta_scope: meta.quoted.advanced_csv
|
||||
- match: (\")
|
||||
captures:
|
||||
1: string.quoted.double.advanced_csv
|
||||
pop: true
|
||||
- include: main
|
||||
- match: '(\[([+-]?\d*)(\:)?([+-]?\d*)(\,)?([+-]?\d*)(\:)?([+-]?\d*)\])?\s*([<>v^])?\s*(=)'
|
||||
captures:
|
||||
1: keyword.operator.advanced_csv
|
||||
2: constant.numeric.formula.advanced_csv
|
||||
4: constant.numeric.formula.advanced_csv
|
||||
6: constant.numeric.formula.advanced_csv
|
||||
8: constant.numeric.formula.advanced_csv
|
||||
9: keyword.operator.advanced_csv
|
||||
10: keyword.operator.advanced_csv
|
||||
push:
|
||||
- meta_scope: meta.range.advanced_csv
|
||||
- match: (?=(\")|$)
|
||||
pop: true
|
||||
- include: scope:source.python
|
||||
- match: '(?<=^|,|\s|\")([0-9.eE+-]+)(?=$|,|\s|\")'
|
||||
scope: meta.number.advanced_csv
|
||||
captures:
|
||||
1: constant.numeric.advanced_csv
|
||||
- match: '(?<=^|,|\s|\")([^, \t\"]+)(?=$|,|\s|\")'
|
||||
scope: meta.nonnumber.advanced_csv
|
||||
captures:
|
||||
1: storage.type.advanced_csv
|
||||
- match: (\,)
|
||||
scope: meta.delimiter.advanced_csv
|
||||
captures:
|
||||
1: keyword.operator.advanced_csv
|
|
@ -131,9 +131,9 @@ contexts:
|
|||
scope: keyword.control.pseudo-method.crystal
|
||||
- match: '\b(nil|true|false)\b(?![?!])'
|
||||
scope: constant.language.crystal
|
||||
- match: '\b(__(DIR|FILE|LINE)__|self)\b(?![?!])'
|
||||
- match: '\b(__(DIR|FILE|LINE|END_LINE)__|self)\b(?![?!])'
|
||||
scope: variable.language.crystal
|
||||
- match: '\b(initialize|new|loop|include|extend|raise|getter|setter|property|class_getter|class_setter|class_property|describe|it|with|delegate|def_hash|def_equals|def_equals_and_hash|forward_missing_to|record|assert_responds_to|spawn)\b[!?]?'
|
||||
- match: '\b(initialize|new|loop|include|extend|raise|getter|setter|property|class_getter|class_setter|class_property|describe|context|it|with|delegate|def_hash|def_equals|def_equals_and_hash|forward_missing_to|record|assert_responds_to|spawn|annotation|verbatim)\b[!?]?'
|
||||
comment: everything being a method but having a special function is a..
|
||||
scope: keyword.control.special-method.crystal
|
||||
- match: \b(require)\b
|
||||
|
@ -180,7 +180,7 @@ contexts:
|
|||
(?<=^|\s)(def)\s+ # the def keyword
|
||||
( (?>[a-zA-Z_\x{80}-\x{10FFFF}][\x{80}-\x{10FFFF}\w]*(?>\.|::))? # a method name prefix
|
||||
(?>[a-zA-Z_\x{80}-\x{10FFFF}][\x{80}-\x{10FFFF}\w]*(?>[?!]|=(?!>))? # the method name
|
||||
|===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\]=?) ) # …or an operator method
|
||||
|===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\](?:=|\?)?) ) # …or an operator method
|
||||
\s*(\() # the openning parenthesis for arguments
|
||||
comment: the method pattern comes from the symbol pattern, see there for a explaination
|
||||
captures:
|
||||
|
@ -201,7 +201,7 @@ contexts:
|
|||
(?<=^|\s)(def)\s+ # the def keyword
|
||||
( (?>[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*(?>\.|::))? # a method name prefix
|
||||
(?>[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*(?>[?!]|=(?!>))? # the method name
|
||||
|===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\]=?) ) # …or an operator method
|
||||
|===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\](?:=|\?)?) ) # …or an operator method
|
||||
[ \t] # the space separating the arguments
|
||||
(?=[ \t]*[^\s#;]) # make sure arguments and not a comment follow
|
||||
comment: same as the previous rule, but without parentheses around the arguments
|
||||
|
@ -221,7 +221,7 @@ contexts:
|
|||
( \s+ # an optional group of whitespace followed by…
|
||||
( (?>[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*(?>\.|::))? # a method name prefix
|
||||
(?>[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*(?>[?!]|=(?!>))? # the method name
|
||||
|===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\]=?) ) )? # …or an operator method
|
||||
|===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\](?:=|\?)?) ) )? # …or an operator method
|
||||
comment: the optional name is just to catch the def also without a method-name
|
||||
scope: meta.function.method.without-arguments.crystal
|
||||
captures:
|
||||
|
@ -588,7 +588,7 @@ contexts:
|
|||
pop: true
|
||||
- match: \\.
|
||||
comment: Cant be named because its not neccesarily an escape.
|
||||
- match: '(?<!:)(:)(?>[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*(?>[?!]|=(?![>=]))?|===?|>[>=]?|<[<=]?|<=>|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\]=?|@@?[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*)'
|
||||
- match: '(?<!:)(:)(?>[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*(?>[?!]|=(?![>=]))?|===?|>[>=]?|<[<=]?|<=>|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\](?:=|\?)?|@@?[a-zA-Z_\x{80}-\x{10FFFF}][\w\x{80}-\x{10FFFF}]*|!=?(?![?!]))'
|
||||
comment: symbols
|
||||
scope: constant.other.symbol.crystal
|
||||
captures:
|
||||
|
@ -713,14 +713,14 @@ contexts:
|
|||
captures:
|
||||
0: punctuation.definition.string.begin.crystal
|
||||
push:
|
||||
- meta_scope: string.unquoted.embedded.js.jquery.crystal
|
||||
- meta_content_scope: text.js.jquery.embedded.crystal
|
||||
- meta_scope: string.unquoted.embedded.js.crystal
|
||||
- meta_content_scope: text.js.embedded.crystal
|
||||
- match: \s*\2$
|
||||
captures:
|
||||
0: punctuation.definition.string.end.crystal
|
||||
pop: true
|
||||
- include: heredoc
|
||||
- include: scope:source.js.jquery
|
||||
- include: scope:source.js
|
||||
- include: interpolated_crystal
|
||||
- include: escaped_char
|
||||
- match: '(?><<-("?)((?:[_\w]+_|)(?:SH|SHELL))\b\1)'
|
|
@ -39,22 +39,31 @@ contexts:
|
|||
- include: comments-doc-oldschool
|
||||
- include: comments-doc
|
||||
- include: comments-inline
|
||||
comments-block:
|
||||
- match: /\*
|
||||
push:
|
||||
- meta_scope: comment.block.dart
|
||||
- match: \*/
|
||||
pop: true
|
||||
- include: comments-block
|
||||
comments-doc:
|
||||
- match: ///
|
||||
scope: comment.block.documentation.dart
|
||||
push:
|
||||
- meta_scope: comment.block.documentation.dart
|
||||
- match: .*
|
||||
pop: true
|
||||
- include: dartdoc
|
||||
comments-doc-oldschool:
|
||||
- match: /\*\*
|
||||
push:
|
||||
- meta_scope: comment.block.documentation.dart
|
||||
- match: \*/
|
||||
pop: true
|
||||
- include: comments-doc-oldschool
|
||||
- include: comments-block
|
||||
- include: dartdoc
|
||||
comments-inline:
|
||||
- match: /\*
|
||||
push:
|
||||
- meta_scope: comment.block.dart
|
||||
- match: \*/
|
||||
pop: true
|
||||
- include: comments-block
|
||||
- match: ((//).*)$
|
||||
captures:
|
||||
1: comment.line.double-slash.dart
|
||||
|
@ -65,16 +74,16 @@ contexts:
|
|||
scope: variable.language.dart
|
||||
- match: '(?<!\$)\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)\b(?!\$)'
|
||||
scope: constant.numeric.dart
|
||||
- match: "(?<![a-zA-Z0-9_$])[_$]*[A-Z][a-zA-Z0-9_$]*"
|
||||
- match: '(?<![a-zA-Z0-9_$])([_$]*[A-Z][a-zA-Z0-9_$]*|bool\b|num\b|int\b|double\b|dynamic\b)'
|
||||
scope: support.class.dart
|
||||
- match: '([_$]*[a-z][a-zA-Z0-9_$]*)(\(|\s+=>)'
|
||||
- match: '([_$]*[a-z][a-zA-Z0-9_$]*)(<|\(|\s+=>)'
|
||||
captures:
|
||||
1: entity.name.function.dart
|
||||
dartdoc:
|
||||
- match: '(\[.*?\])'
|
||||
captures:
|
||||
0: variable.name.source.dart
|
||||
- match: " .*"
|
||||
- match: '^ {4,}(?![ \*]).*'
|
||||
captures:
|
||||
0: variable.name.source.dart
|
||||
- match: "```.*?$"
|
||||
|
@ -105,7 +114,7 @@ contexts:
|
|||
scope: keyword.control.dart
|
||||
- match: (?<!\$)\b(new)\b(?!\$)
|
||||
scope: keyword.control.new.dart
|
||||
- match: (?<!\$)\b(abstract|class|enum|extends|external|factory|implements|get|mixin|native|operator|set|typedef|with)\b(?!\$)
|
||||
- match: (?<!\$)\b(abstract|class|enum|extends|external|factory|implements|get|mixin|native|operator|set|typedef|with|covariant)\b(?!\$)
|
||||
scope: keyword.declaration.dart
|
||||
- match: (?<!\$)\b(is\!?)\b(?!\$)
|
||||
scope: keyword.operator.dart
|
||||
|
@ -131,7 +140,7 @@ contexts:
|
|||
scope: keyword.operator.logical.dart
|
||||
- match: (?<!\$)\b(static|final|const)\b(?!\$)
|
||||
scope: storage.modifier.dart
|
||||
- match: (?<!\$)\b(?:void|bool|num|int|double|dynamic|var)\b(?!\$)
|
||||
- match: (?<!\$)\b(?:void|var)\b(?!\$)
|
||||
scope: storage.type.primitive.dart
|
||||
punctuation:
|
||||
- match: ","
|
1
sublime/syntaxes/Docker.tmbundle
Submodule
1
sublime/syntaxes/Docker.tmbundle
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 9e9a518aed93031042c54710f8f02c839301de26
|
1
sublime/syntaxes/GraphQL-SublimeText3
Submodule
1
sublime/syntaxes/GraphQL-SublimeText3
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit c9d84587eb1a6eb34457a875f21b9b1a29306be3
|
1
sublime/syntaxes/Julia-sublime
Submodule
1
sublime/syntaxes/Julia-sublime
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 6c0d770fc74e6bc037c70cae1f94fe113b60fd95
|
|
@ -16,18 +16,19 @@ contexts:
|
|||
- include: imports
|
||||
- include: statements
|
||||
classes:
|
||||
- match: (?=\s*(?:companion|class|object|interface))
|
||||
- match: (?<!::)(?=\b(?:companion|class|object|interface)\b)
|
||||
push:
|
||||
- match: "}|(?=$)"
|
||||
- match: '(?=$|\})'
|
||||
pop: true
|
||||
- include: comments
|
||||
- match: \b(companion\s*)?(class|object|interface)\b
|
||||
captures:
|
||||
1: keyword.other.kotlin
|
||||
1: storage.modifier.kotlin
|
||||
2: storage.modifier.kotlin
|
||||
push:
|
||||
- match: '(?=<|{|\(|:)'
|
||||
- match: '(?=<|\{|\(|:|$)'
|
||||
pop: true
|
||||
- match: \b(object)\b
|
||||
scope: keyword.other.kotlin
|
||||
- include: comments
|
||||
- match: \w+
|
||||
scope: entity.name.type.class.kotlin
|
||||
- match: <
|
||||
|
@ -75,7 +76,7 @@ contexts:
|
|||
constants:
|
||||
- match: \b(true|false|null|this|super)\b
|
||||
scope: constant.language.kotlin
|
||||
- match: '\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)([LlFfUuDd]|UL|ul)?\b'
|
||||
- match: '\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)([LlFf])?\b'
|
||||
scope: constant.numeric.kotlin
|
||||
- match: '\b([A-Z][A-Z0-9_]+)\b'
|
||||
scope: constant.other.kotlin
|
||||
|
@ -91,9 +92,9 @@ contexts:
|
|||
- include: comments
|
||||
- include: keywords
|
||||
functions:
|
||||
- match: (?=\s*(?:fun))
|
||||
- match: (?=\s*\b(?:fun)\b)
|
||||
push:
|
||||
- match: "}|(?=$)"
|
||||
- match: '(?=$|\})'
|
||||
pop: true
|
||||
- match: \b(fun)\b
|
||||
captures:
|
||||
|
@ -192,32 +193,40 @@ contexts:
|
|||
1: keyword.other.kotlin
|
||||
2: keyword.other.kotlin
|
||||
keywords:
|
||||
- match: \b(var|val|public|private|protected|abstract|final|enum|open|attribute|annotation|override|inline|var|val|vararg|lazy|in|out|internal|data|tailrec|operator|infix|const|yield|typealias|typeof)\b
|
||||
- match: \b(var|val|public|private|protected|abstract|final|sealed|enum|open|attribute|annotation|override|inline|vararg|in|out|internal|data|tailrec|operator|infix|const|yield|typealias|typeof|reified|suspend)\b
|
||||
scope: storage.modifier.kotlin
|
||||
- match: \b(try|catch|finally|throw)\b
|
||||
scope: keyword.control.catch-exception.kotlin
|
||||
- match: \b(if|else|while|for|do|return|when|where|break|continue)\b
|
||||
scope: keyword.control.kotlin
|
||||
- match: \b(in|is|as|assert)\b
|
||||
- match: \b(in|is|!in|!is|as|as\?|assert)\b
|
||||
scope: keyword.operator.kotlin
|
||||
- match: (==|!=|===|!==|<=|>=|<|>)
|
||||
scope: keyword.operator.comparison.kotlin
|
||||
- match: (=)
|
||||
scope: keyword.operator.assignment.kotlin
|
||||
- match: (::)
|
||||
scope: keyword.operator.kotlin
|
||||
- match: (:)
|
||||
scope: keyword.operator.declaration.kotlin
|
||||
- match: \b(by)\b
|
||||
scope: keyword.other.by.kotlin
|
||||
- match: (\?\.)
|
||||
scope: keyword.operator.safenav.kotlin
|
||||
- match: (\.)
|
||||
scope: keyword.operator.dot.kotlin
|
||||
- match: (\?:)
|
||||
scope: keyword.operator.elvis.kotlin
|
||||
- match: (\-\-|\+\+)
|
||||
scope: keyword.operator.increment-decrement.kotlin
|
||||
- match: (\-|\+|\*|\/|%)
|
||||
scope: keyword.operator.arithmetic.kotlin
|
||||
- match: (\+=|\-=|\*=|\/=)
|
||||
scope: keyword.operator.arithmetic.assign.kotlin
|
||||
- match: (!|&&|\|\|)
|
||||
scope: keyword.operator.logical.kotlin
|
||||
- match: (\.\.)
|
||||
scope: keyword.operator.range.kotlin
|
||||
- match: (\-|\+|\*|\/|%)
|
||||
scope: keyword.operator.arithmetic.kotlin
|
||||
- match: (!|&&|\|\|)
|
||||
scope: keyword.operator.logical.kotlin
|
||||
- match: (;)
|
||||
scope: punctuation.terminator.kotlin
|
||||
namespaces:
|
||||
|
@ -315,11 +324,11 @@ contexts:
|
|||
- include: generics
|
||||
- include: expressions
|
||||
types:
|
||||
- match: \b(Any|Unit|String|Int|Boolean|Char|Long|Double|Float|Short|Byte|dynamic)\b
|
||||
- match: \b(Nothing|Any|Unit|String|CharSequence|Int|Boolean|Char|Long|Double|Float|Short|Byte|dynamic)\b
|
||||
scope: storage.type.buildin.kotlin
|
||||
- match: \b(IntArray|BooleanArray|CharArray|LongArray|DoubleArray|FloatArray|ShortArray|ByteArray)\b
|
||||
scope: storage.type.buildin.array.kotlin
|
||||
- match: \b(Array|List|Map)<\b
|
||||
- match: \b(Array|Collection|List|Map|Set|MutableList|MutableMap|MutableSet|Sequence)<\b
|
||||
captures:
|
||||
1: storage.type.buildin.collection.kotlin
|
||||
push:
|
||||
|
@ -333,13 +342,6 @@ contexts:
|
|||
pop: true
|
||||
- include: types
|
||||
- include: keywords
|
||||
- match: (#)\(
|
||||
captures:
|
||||
1: keyword.operator.tuple.kotlin
|
||||
push:
|
||||
- match: \)
|
||||
pop: true
|
||||
- include: expressions
|
||||
- match: '\{'
|
||||
push:
|
||||
- match: '\}'
|
||||
|
@ -353,15 +355,15 @@ contexts:
|
|||
- match: (->)
|
||||
scope: keyword.operator.declaration.kotlin
|
||||
variables:
|
||||
- match: (?=\s*(?:var|val))
|
||||
- match: (?=\s*\b(?:var|val)\b)
|
||||
push:
|
||||
- match: (?=:|=|$)
|
||||
- match: (?=:|=|(\b(by)\b)|$)
|
||||
pop: true
|
||||
- match: \b(var|val)\b
|
||||
captures:
|
||||
1: keyword.other.kotlin
|
||||
push:
|
||||
- match: (?=:|=|$)
|
||||
- match: (?=:|=|(\b(by)\b)|$)
|
||||
pop: true
|
||||
- match: <
|
||||
push:
|
||||
|
@ -379,6 +381,13 @@ contexts:
|
|||
pop: true
|
||||
- include: types
|
||||
- include: getters-and-setters
|
||||
- match: \b(by)\b
|
||||
captures:
|
||||
1: keyword.other.kotlin
|
||||
push:
|
||||
- match: (?=$)
|
||||
pop: true
|
||||
- include: expressions
|
||||
- match: (=)
|
||||
captures:
|
||||
1: keyword.operator.assignment.kotlin
|
1
sublime/syntaxes/LESS-sublime
Submodule
1
sublime/syntaxes/LESS-sublime
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 3dd952ea771e5bc087a41146941ed36f2051c3c4
|
1
sublime/syntaxes/Packages
Submodule
1
sublime/syntaxes/Packages
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit f36b8f807d5f30d2b8ef639232a9fc5960f550fa
|
|
@ -147,7 +147,7 @@ contexts:
|
|||
2: keyword.other.powershell
|
||||
3: variable.parameter.powershell
|
||||
attribute:
|
||||
- match: '(\[)\s*\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength)\b'
|
||||
- match: '(\[)\s*\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength|supportswildcards)\b'
|
||||
captures:
|
||||
1: punctuation.section.bracket.begin.powershell
|
||||
2: support.function.attribute.powershell
|
||||
|
@ -165,31 +165,11 @@ contexts:
|
|||
captures:
|
||||
0: punctuation.section.group.end.powershell
|
||||
pop: true
|
||||
- include: variable
|
||||
- include: variableNoProperty
|
||||
- include: hashtable
|
||||
- include: scriptblock
|
||||
- include: doubleQuotedStringEscapes
|
||||
- include: doubleQuotedString
|
||||
- include: type
|
||||
- include: numericConstant
|
||||
- include: doubleQuotedString
|
||||
- include: main
|
||||
- match: (?i)\b(mandatory|valuefrompipeline|valuefrompipelinebypropertyname|valuefromremainingarguments|position|parametersetname|defaultparametersetname|supportsshouldprocess|supportspaging|positionalbinding|helpuri|confirmimpact|helpmessage)\b(?:\s+)?(=)?
|
||||
captures:
|
||||
1: variable.parameter.attribute.powershell
|
||||
2: keyword.operator.assignment.powershell
|
||||
- match: (?<!')'
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.powershell
|
||||
push:
|
||||
- meta_scope: string.quoted.single.powershell
|
||||
- match: "'(?!')"
|
||||
captures:
|
||||
0: punctuation.definition.string.end.powershell
|
||||
pop: true
|
||||
- match: "''"
|
||||
scope: constant.character.escape.powershell
|
||||
commands:
|
||||
- match: '(?:(\p{L}|\d|_|-|\\|\:)*\\)?\b(?i:Add|Approve|Assert|Backup|Block|Build|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Deploy|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Write)\-.+?(?:\.(?i:exe|cmd|bat|ps1))?\b'
|
||||
comment: "Verb-Noun pattern:"
|
||||
|
@ -242,14 +222,14 @@ contexts:
|
|||
pop: true
|
||||
- match: '(?i)\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,64}\b'
|
||||
- include: variableNoProperty
|
||||
- include: variable
|
||||
- include: doubleQuotedStringEscapes
|
||||
- include: interpolation
|
||||
- match: '`\s*$'
|
||||
scope: keyword.other.powershell
|
||||
doubleQuotedStringEscapes:
|
||||
- match: '`[0abnfrvt"''$`]'
|
||||
- match: '`[`0abefnrtv"''$]'
|
||||
scope: constant.character.escape.powershell
|
||||
- include: unicodeEscape
|
||||
- match: '""'
|
||||
scope: constant.character.escape.powershell
|
||||
function:
|
||||
|
@ -362,6 +342,11 @@ contexts:
|
|||
- match: '(?!\d+|\.)(?:\p{L}|\p{N}|\.)+'
|
||||
scope: storage.type.powershell
|
||||
- include: main
|
||||
unicodeEscape:
|
||||
- match: '`u\{(?:(?:10)?([0-9a-fA-F]){1,4}|0?{{1}}{1,5})}'
|
||||
scope: constant.character.escape.powershell
|
||||
- match: '`u(?:\{[0-9a-fA-F]{,6}.)?'
|
||||
scope: invalid.character.escape.powershell
|
||||
variable:
|
||||
- match: (\$)(?i:(False|Null|True))\b
|
||||
comment: These are special constants.
|
||||
|
@ -374,13 +359,13 @@ contexts:
|
|||
0: support.constant.variable.powershell
|
||||
1: punctuation.definition.variable.powershell
|
||||
3: variable.other.member.powershell
|
||||
- match: '(\$)(?i:(\$|\^|\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))((?:\.(?:\p{L}|\d|_)+)*\b)?\b'
|
||||
- match: '(\$)((?:[$^?])|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\b)((?:\.(?:\p{L}|\d|_)+)*\b)?'
|
||||
comment: Automatic variables are not constants, but they are read-only. In monokai (default) color schema support.variable doesn't have color, so we use constant.
|
||||
captures:
|
||||
0: support.constant.automatic.powershell
|
||||
0: support.variable.automatic.powershell
|
||||
1: punctuation.definition.variable.powershell
|
||||
3: variable.other.member.powershell
|
||||
- match: '(\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))((?:\.(?:\p{L}|\d|_)+)*\b)?\b'
|
||||
- match: '(\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|InformationPreference|LogCommandHealthEvent|LogCommandLifecycleEvent|LogEngineHealthEvent|LogEngineLifecycleEvent|LogProviderHealthEvent|LogProviderLifecycleEvent|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|PSCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoLoadingPreference|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|ProgressPreference|VerbosePreference|WarningPreference|WhatIfPreference))((?:\.(?:\p{L}|\d|_)+)*\b)?\b'
|
||||
comment: Style preference variables as language variables so that they stand out.
|
||||
captures:
|
||||
0: variable.language.powershell
|
||||
|
@ -426,19 +411,19 @@ contexts:
|
|||
0: support.constant.variable.powershell
|
||||
1: punctuation.definition.variable.powershell
|
||||
3: variable.other.member.powershell
|
||||
- match: (\$)(?i:(\$|\^|\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))\b
|
||||
- match: '(\$)((?:[$^?])|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\b)'
|
||||
comment: Automatic variables are not constants, but they are read-only...
|
||||
captures:
|
||||
0: support.variable.automatic.powershell
|
||||
1: punctuation.definition.variable.powershell
|
||||
3: variable.other.member.powershell
|
||||
- match: (\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|ProgressPreference|PsCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|VerbosePreference|WarningPreference|WhatIfPreference))\b
|
||||
- match: (\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|InformationPreference|LogCommandHealthEvent|LogCommandLifecycleEvent|LogEngineHealthEvent|LogEngineLifecycleEvent|LogProviderHealthEvent|LogProviderLifecycleEvent|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|PSCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoLoadingPreference|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|ProgressPreference|VerbosePreference|WarningPreference|WhatIfPreference))\b
|
||||
comment: Style preference variables as language variables so that they stand out.
|
||||
captures:
|
||||
0: variable.language.powershell
|
||||
1: punctuation.definition.variable.powershell
|
||||
3: variable.other.member.powershell
|
||||
- match: '(?i:(\$|@)(global|local|private|script|using|workflow):((?:\p{L}|\d|_)+))'
|
||||
- match: '(?i:(\$)(global|local|private|script|using|workflow):((?:\p{L}|\d|_)+))'
|
||||
captures:
|
||||
0: variable.other.readwrite.powershell
|
||||
1: punctuation.definition.variable.powershell
|
18
sublime/syntaxes/README.md
Normal file
18
sublime/syntaxes/README.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Sublime syntaxes
|
||||
|
||||
All the submodules are offering .sublime-syntax files. The ones that are not in a submodule
|
||||
are converted from the following repos at the following commit:
|
||||
|
||||
- Assembly x86: https://github.com/Nessphoro/sublimeassembly/tree/150352f4d89d7fde48b18cc4b046385a6e926c96
|
||||
- CSV: https://github.com/wadetb/Sublime-Text-Advanced-CSV/tree/4786d037a761bc45e516f6b0624a839919ec6d05
|
||||
- Crystal: https://github.com/crystal-lang-tools/sublime-crystal/tree/2ee9d667aeb9e90846244b5a74e78826014676de
|
||||
- Dart: https://github.com/LiteCatDev/Dartlight/tree/4a9abb5d480eec6b4b95fe6ff4667162a33d41d6
|
||||
- Kotlin: https://github.com/vkostyukov/kotlin-sublime-package/tree/aeeed2780b04aea3d293c547c24cae27cafef0c5
|
||||
- Nix: https://github.com/wmertens/sublime-nix/tree/9032bd613746b9c135223fd6f26a5fa555f18946
|
||||
- PowerShell: https://github.com/PowerShell/EditorSyntax/tree/4a0a076661d26473cac71b9a17e6a759e9b1c643
|
||||
- Swift: https://github.com/quiqueg/Swift-Sublime-Package/tree/fef17119ceef28a3c4b8cf72268d6192b595365d
|
||||
- Reason: https://github.com/reasonml-editor/sublime-reason/tree/69925396ec6e73581357c0b1c487055fa6c065e8
|
||||
- Stylus: https://github.com/billymoon/Stylus/tree/30908e3b5757d6cab4bf2ce660ef89b0c614cf62
|
||||
- Racket: https://gist.githubusercontent.com/sliminality/3f1527f8e910c36b3303346422b03409/raw/9ce1ddd463c9bc97439726fd5f63b1fb3a36419b/Racket.tmLanguage
|
||||
|
||||
The others, I haven't kept track of the repo/commit.
|
52
sublime/syntaxes/Racket.sublime-syntax
Normal file
52
sublime/syntaxes/Racket.sublime-syntax
Normal file
|
@ -0,0 +1,52 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Racket
|
||||
file_extensions:
|
||||
- rkt
|
||||
scope: source.racket
|
||||
contexts:
|
||||
main:
|
||||
- match: '[^\\](\"[^\"]*\")'
|
||||
captures:
|
||||
1: string.quoted.double.source.racket
|
||||
- match: '\((define)\s+([a-zA-Z0-9_\-?\+^:/!]+)\s*'
|
||||
scope: meta.variable.source.racket
|
||||
captures:
|
||||
1: keyword.source.racket
|
||||
2: entity.name.variable.source.racket
|
||||
- match: '\((define)\s+\(([a-zA-Z0-9_\-?\+^:/!]+)\s*'
|
||||
scope: meta.function.source.racket
|
||||
captures:
|
||||
1: keyword.source.racket
|
||||
2: entity.name.function
|
||||
- match: '\((struct)\s+([a-zA-Z0-9_\-?\+^]+)\s+'
|
||||
scope: meta.struct.source.racket
|
||||
captures:
|
||||
1: keyword.source.racket
|
||||
2: entity.name.type
|
||||
- match: '[\s\(](if|lambda|cond|define|type-case|let|letrec|let!|\#lang|require|test|else|first|rest|define-type|define-type-alias|define-struct|not|local|error|lang|module|module*|module+|require|provide|quote|#%datum|#%expression|#%top|#%variable-reference|#%app|lambda|case-lambda|let|let*|letrec|let-values|let*-values|let-syntax|letrec-syntax|let-syntaxes|letrec-syntaxes|letrec-syntaxes+values|local|shared|if|cond|and|or|case|define|else|=>|define|define-values|define-syntax|define-syntaxes|define-for-syntax|define-require-syntax|define-provide-syntax|define-syntax-rule|define-record-type|begin|begin0|begin-for-syntax|when|unless|set!|set!-values|for|for/list|for/vector|for/hash|for/hasheq|for/hasheqv|for/and|for/or|for/lists|for/first|for/last|for/fold|for*|for*/list|for*/vector|for*/hash|for*/hasheq|for*/hasheqv|for*/and|for*/or|for*/lists|for*/first|for*/last|for*/fold|for/fold/derived|for*/fold/derived|define-sequence-syntax|:do-in|do|with-continuation-mark|quasiquote|unquote|unquote-splicing|quote-syntax|#%top-interaction|define-package|open-package|package-begin|define*|define*-values|define*-syntax|define*-syntaxes|open*-package|package?|package-exported-identifiers|package-original-identifiers|block|#%stratified-body|match|match*|match/values|define/match|match-lambda|match-lambda*|match-lambda**|match-let|match-let*|match-let-values|match-let*-values|match-letrec|match-define|match-define-values|with-handlers|with-handlers*|let/cc|let/ec|%|prompt|control|prompt-at|control-at|reset|shift|reset-at|shift-at|prompt0|reset0|control0|shift0|prompt0-at|reset0-at|control0-at|shift0-at|set|cupto|write|display|displayln|print|fprintf|printf|eprintf|format|print-pair-curly-braces|print-mpair-curly-braces|print-unreadable|print-graph|print-struct|print-box|print-vector-length|print-hash-table|print-boolean-long-form|print-reader-abbreviations|print-as-expression|print-syntax-width|current-write-relative-directory|port-write-handler|port-display-handler|port-print-handler|global-port-print-handler)[\s\)]'
|
||||
scope: meta.keywords.source.racket
|
||||
captures:
|
||||
1: keyword.source.racket
|
||||
- match: '[\s\(](true|false|empty|null)[\s\)]'
|
||||
captures:
|
||||
1: constant.language.source.racket
|
||||
- match: '[\s\(\{\[](#t|#f|#true|#false)[\s\)\}\]]'
|
||||
captures:
|
||||
1: constant.language.source.racket
|
||||
- match: '(#\\[a-zA-Z0-9_\-?\+\.\!\"]+)'
|
||||
captures:
|
||||
1: constant.language.source.racket
|
||||
- match: '\b(0|([1-9][0-9_]*))\b'
|
||||
scope: constant.numeric.integer.source.racket
|
||||
- match: ;
|
||||
push:
|
||||
- meta_scope: comment.line.documentation.source.racket
|
||||
- match: $\n
|
||||
pop: true
|
||||
- match: '#\|'
|
||||
push:
|
||||
- meta_scope: comment.block.source.racket
|
||||
- match: '\|#'
|
||||
pop: true
|
|
@ -10,6 +10,7 @@ contexts:
|
|||
main:
|
||||
- include: reason_comment_doc_block
|
||||
- include: reason_comment_block
|
||||
- include: reason_comment
|
||||
- include: reason_named_arg
|
||||
- include: reason_module
|
||||
- include: reason_lifetime
|
||||
|
@ -102,6 +103,12 @@ contexts:
|
|||
scope: storage.modifier.rec.source.reason
|
||||
- match: \bmodule\b
|
||||
scope: storage.modifier.module.source.reason
|
||||
reason_comment:
|
||||
- match: //
|
||||
push:
|
||||
- meta_scope: comment.source.reason
|
||||
- match: (?=^)
|
||||
pop: true
|
||||
reason_comment_block:
|
||||
- match: /\*
|
||||
push:
|
711
sublime/syntaxes/Stylus.sublime-syntax
Normal file
711
sublime/syntaxes/Stylus.sublime-syntax
Normal file
|
@ -0,0 +1,711 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Stylus
|
||||
file_extensions:
|
||||
- styl
|
||||
- stylus
|
||||
scope: source.stylus
|
||||
contexts:
|
||||
main:
|
||||
- include: comments
|
||||
- match: '^\s*(@(?:import|charset|css|font-face|(?:-webkit-)?keyframes)(?:\s+([\w-]+))?)\b'
|
||||
captures:
|
||||
1: keyword.control.at-rule.other.stylus
|
||||
2: variable.other.animation-name.stylus
|
||||
push:
|
||||
- match: '$|;|(?=\{)'
|
||||
pop: true
|
||||
- include: string-quoted
|
||||
- match: ^\s*(@media)\s*
|
||||
captures:
|
||||
1: keyword.control.at-rule.media.stylus
|
||||
push:
|
||||
- match: '$|(?=\{)'
|
||||
pop: true
|
||||
- include: media-query
|
||||
- match: |-
|
||||
(?x)
|
||||
(?<=^|;|})
|
||||
\s*
|
||||
(?=
|
||||
[\[\]'".\w$-]+
|
||||
\s*
|
||||
([?:]?=)
|
||||
(?![^\[]*\])
|
||||
)
|
||||
push:
|
||||
- match: $|;
|
||||
pop: true
|
||||
- include: expression
|
||||
- include: iteration
|
||||
- include: conditionals
|
||||
- include: return
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
|
||||
^(\s*) # starts at the beginning of line
|
||||
([\w$-]+) # identifier (name)
|
||||
(\() # start of argument list
|
||||
(?=
|
||||
.*?
|
||||
\)\s*\{ # we see a curly brace afterwards
|
||||
) # which means this is a function definition
|
||||
captures:
|
||||
2: entity.name.function.stylus
|
||||
3: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(
|
||||
|
||||
(^|;) # starts at the beginning of line or at a ;
|
||||
\s*
|
||||
(\+?\s* # for block mixins
|
||||
[\w$-]+) # identifier (name)
|
||||
(\() # start of argument list
|
||||
(?=
|
||||
.*?
|
||||
\)\s*;?\s* # if there are only spaces and semicolons
|
||||
$|; # then this a
|
||||
)
|
||||
)
|
||||
captures:
|
||||
3: entity.other.attribute-name.mixin.stylus
|
||||
4: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(^|(?<=\*/|\}))\s*
|
||||
(?=
|
||||
font(?!
|
||||
\s*:\s
|
||||
|
|
||||
-
|
||||
|
|
||||
.*?
|
||||
(?:
|
||||
\/|normal|bold|light(er?)|serif|sans|monospace|
|
||||
\b\d+(?:\b|px|r?em|%)|
|
||||
var\s*\(|
|
||||
['"][^\]]*$
|
||||
)
|
||||
) | # we need to distinguish between tag and property `cursor`
|
||||
cursor(?!
|
||||
\s*[:;]\s
|
||||
|
|
||||
-
|
||||
|
|
||||
.*?
|
||||
(?:
|
||||
(?:url\s*\()|
|
||||
(?:-moz-|-webkit-|-ms-)?
|
||||
(?:auto|default|none|context-menu|help|pointer|progress|
|
||||
wait|cell|crosshair|text|vertical-text|alias|copy|
|
||||
move|no-drop|not-allowed|e-resize|n-resize|ne-resize|
|
||||
nw-resize|s-resize|se-resize|sw-resize|w-resize|
|
||||
ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|
|
||||
row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing
|
||||
normal|bold|light(er?)|serif|sans|monospace)
|
||||
)
|
||||
) | (
|
||||
(
|
||||
altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|
|
||||
animateMotion|animateTransform|circle|clipPath|color-profile|
|
||||
defs|desc|ellipse|feBlend|feColorMatrix|
|
||||
feComponentTransfer|feComposite|feConvolveMatrix|
|
||||
feDiffuseLighting|feDisplacementMap|feDistantLight|feFlood|
|
||||
feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|
|
||||
feMergeNode|feMorphology|feOffset|fePointLight|
|
||||
feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|
|
||||
font-face|font-face-format|font-face-name|font-face-src|
|
||||
font-face-uri|foreignObject|g|glyph|glyphRef|hkern|image|line|
|
||||
linearGradient|marker|mask|metadata|missing-glyph|mpath|path|
|
||||
pattern|polygon|polyline|radialGradient|rect|set|stop|svg|
|
||||
switch|symbol|text|textPath|tref|tspan|use|view|vkern|
|
||||
a|abbr|acronym|address|applet|area|article|aside|audio|b|base|
|
||||
basefont|bdi|bdo|bgsound|big|blink|blockquote|body|br|button|
|
||||
canvas|caption|center|cite|code|col|colgroup|data|
|
||||
datalist|dd|decorator|del|details|dfn|dir|div|dl|dt|element|
|
||||
em|embed|fieldset|figcaption|figure|footer|form|frame|
|
||||
frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|
|
||||
img|input|ins|isindex|kbd|keygen|label|legend|li|link|listing|
|
||||
main|map|mark|marquee|menu|menuitem|meta|meter|nav|nobr|
|
||||
noframes|noscript|object|ol|optgroup|option|output|p|param|
|
||||
plaintext|pre|progress|q|rp|rt|ruby|s|samp|script|section|
|
||||
select|shadow|small|source|spacer|span|strike|strong|style|
|
||||
sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|
|
||||
thead|time|title|tr|track|tt|u|ul|var|video|wbr|xmp)
|
||||
|
||||
\s*([\s,.#\[]|:[^\s]|(?=\{|$))
|
||||
|
||||
) | (
|
||||
[:~>\[*\/] # symbols but they are valid for selector
|
||||
|
||||
) | (
|
||||
|
||||
\+\s*[\w$-]+\b\s* # are an identifier starting with $
|
||||
(?!\() # and they can't have anything besides
|
||||
|
||||
) | ( # for animtions
|
||||
|
||||
\d+(\.\d+)?%|(from|to)\b
|
||||
|
||||
) | ( # Placeholder selectors
|
||||
|
||||
\$[\w$-]+\b\s* # are an identifier starting with $
|
||||
(?=$|\{) # and they can't have anything besides
|
||||
|
||||
) | ( # CSS class
|
||||
|
||||
\.[a-zA-Z0-9_-]+
|
||||
|
||||
) | ( # CSS id
|
||||
|
||||
\#[a-zA-Z0-9_-]+
|
||||
|
||||
) | ( # Reference to parent
|
||||
|
||||
([\w\d_-]+)? # matching any word right before &
|
||||
(&) # & itself, escaped because of plist
|
||||
([\w\d_-]+)? # matching any word right after &
|
||||
)
|
||||
)
|
||||
push:
|
||||
- meta_scope: meta.selector.stylus
|
||||
- match: |-
|
||||
|
||||
|$|(?=\{\s*\}.*$)|(?=\{.*?[:;])|(?=\{)(?!.+\}.*$)
|
||||
pop: true
|
||||
- include: comma
|
||||
- match: \d+(\.\d+)?%|from|to
|
||||
scope: entity.other.animation-keyframe.stylus
|
||||
- include: selector-components
|
||||
- match: .
|
||||
scope: entity.other.attribute-name.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(?<=^|;|{)\s* # starts after begining of line, '{' or ';''
|
||||
(?= # lookahead for
|
||||
(
|
||||
[a-zA-Z0-9_-] # then a letter
|
||||
| # or
|
||||
(\{(.*?)\}) # interpolation
|
||||
| # or
|
||||
(/\*.*?\*/) # comment
|
||||
)+
|
||||
|
||||
\s*[:\s]\s* # value is separted by colon or space
|
||||
|
||||
(?!(\s*\{)) # if there are only spaces afterwards
|
||||
|
||||
(?!
|
||||
[^}]*? # checking for an unclosed curly braces on this
|
||||
\{ # line because if one exists it means that
|
||||
[^}]* # this is a selector and not a property
|
||||
($|\})
|
||||
)
|
||||
)
|
||||
push:
|
||||
- match: '(?=\}|;)|(?<!,)\s*\n'
|
||||
pop: true
|
||||
- include: comments
|
||||
- include: interpolation
|
||||
- match: '(?<!^|;|{)\s*(?:(:)|\s)'
|
||||
captures:
|
||||
1: punctuation.separator.key-value.stylus
|
||||
push:
|
||||
- match: '(;)|(?=\})|(?=(?<!\,)\s*\n)'
|
||||
captures:
|
||||
1: punctuation.terminator.rule.stylus
|
||||
pop: true
|
||||
- include: comments
|
||||
- include: expression
|
||||
- match: "-(moz|o|ms|webkit|khtml)-"
|
||||
scope: support.type.vendor-prefix.stylus
|
||||
- match: .
|
||||
scope: meta.property-name.stylus support.type.property-name.stylus
|
||||
- match: '@extends?\s'
|
||||
captures:
|
||||
0: keyword.language.stylus
|
||||
push:
|
||||
- match: (?=$|;)
|
||||
pop: true
|
||||
- include: selector-components
|
||||
- include: string-quoted
|
||||
- include: escape
|
||||
- include: language-constants
|
||||
- include: language-operators
|
||||
- include: language-keywords
|
||||
- include: property-reference
|
||||
- include: function-call
|
||||
- match: '\{'
|
||||
scope: punctuation.section.start.stylus
|
||||
- match: '\}'
|
||||
scope: punctuation.section.end.stylus
|
||||
attribute-selector:
|
||||
- match: '\[(?=[^\]]*\])'
|
||||
captures:
|
||||
0: punctuation.definition.entity.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.attribute-selector.stylus
|
||||
- match: '\]'
|
||||
captures:
|
||||
0: punctuation.definition.entity.end.stylus
|
||||
pop: true
|
||||
- match: '(?<=\[)|(?<=\{)'
|
||||
push:
|
||||
- match: '(?=[|~=\]\s])'
|
||||
pop: true
|
||||
- include: interpolation
|
||||
- match: .
|
||||
captures:
|
||||
0: entity.other.attribute-name.stylus
|
||||
- include: interpolation
|
||||
- match: "([|~]?=)"
|
||||
captures:
|
||||
1: keyword.operator.stylus
|
||||
- include: string-quoted
|
||||
- match: .
|
||||
captures:
|
||||
0: string.unquoted.stylus
|
||||
color-values:
|
||||
- match: \b(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)\b
|
||||
scope: constant.color.w3c-standard-color-name.stylus
|
||||
- match: (hsla?|rgba?)\s*(\()
|
||||
captures:
|
||||
1: keyword.language.function.misc.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
\b
|
||||
(?:0*((?:1?[0-9]{1,2})|(?:2(?:[0-4][0-9]|5[0-5])))\s*(,)\s*)
|
||||
(?:0*((?:1?[0-9]{1,2})|(?:2(?:[0-4][0-9]|5[0-5])))\s*(,)\s*)
|
||||
(?:0*((?:1?[0-9]{1,2})|(?:2(?:[0-4][0-9]|5[0-5])))\b)
|
||||
captures:
|
||||
1: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.red.stylus
|
||||
2: punctuation.delimiter.comma.stylus
|
||||
3: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.green.stylus
|
||||
4: punctuation.delimiter.comma.stylus
|
||||
5: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.blue.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
\b
|
||||
((?:[0-9]{1,2}|100)%)(,) # red
|
||||
\s*
|
||||
((?:[0-9]{1,2}|100)%)(,) # green
|
||||
\s*
|
||||
((?:[0-9]{1,2}|100)%) # blue
|
||||
captures:
|
||||
1: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.red.stylus
|
||||
2: punctuation.delimiter.comma.stylus
|
||||
3: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.green.stylus
|
||||
4: punctuation.delimiter.comma.stylus
|
||||
5: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.blue.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(?:\s*(,)\s*((0?\.[0-9]+)|[0-1]))?
|
||||
captures:
|
||||
1: punctuation.delimiter.comma.stylus
|
||||
2: constant.other.color.rgb-value.stylus constant.other.color.rgb-value.alpha.stylus
|
||||
- include: numeric-values
|
||||
- include: numeric-values
|
||||
comma:
|
||||
- match: \s*,\s*
|
||||
scope: punctuation.delimiter.comma.stylus
|
||||
comments:
|
||||
- include: single-line-comment
|
||||
- match: \/\*
|
||||
captures:
|
||||
0: punctuation.definition.comment.stylus
|
||||
push:
|
||||
- meta_scope: comment.block.stylus
|
||||
- match: \*\/
|
||||
captures:
|
||||
0: punctuation.definition.comment.stylus
|
||||
pop: true
|
||||
conditionals:
|
||||
- match: '(^\s*|\s+)(if|unless|else)(?=[\s({]|$)\s*'
|
||||
captures:
|
||||
2: keyword.control.stylus
|
||||
push:
|
||||
- match: '(?=$|\{)'
|
||||
pop: true
|
||||
- include: expression
|
||||
escape:
|
||||
- match: \\.
|
||||
scope: constant.character.escape.stylus
|
||||
expression:
|
||||
- include: single-line-comment
|
||||
- include: comma
|
||||
- include: iteration
|
||||
- include: conditionals
|
||||
- include: language-operators
|
||||
- include: language-keywords
|
||||
- include: hash-definition
|
||||
- include: color-values
|
||||
- include: url
|
||||
- include: function-call
|
||||
- include: string-quoted
|
||||
- include: escape
|
||||
- include: hash-access
|
||||
- include: language-constants
|
||||
- include: language-property-value-constants
|
||||
- include: property-reference
|
||||
- include: variable
|
||||
function-call:
|
||||
- match: '([\w$-]+)(\()'
|
||||
captures:
|
||||
1: entity.function-name.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
hash-access:
|
||||
- match: '(?=[\w$-]+(?:\.|\[[^\]=]*\]))'
|
||||
push:
|
||||
- meta_scope: meta.hash-access.stylus
|
||||
- match: '(?=[^''''""\[\]\w.$-]|\s|$)'
|
||||
pop: true
|
||||
- match: \.
|
||||
scope: punctuation.delimiter.hash.stylus
|
||||
- match: '\['
|
||||
scope: punctuation.definition.entity.start.stylus
|
||||
- match: '\]'
|
||||
scope: punctuation.definition.entity.end.stylus
|
||||
- include: string-quoted
|
||||
- include: variable
|
||||
hash-definition:
|
||||
- match: '\{'
|
||||
captures:
|
||||
0: punctuation.section.embedded.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.hash.stylus
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.section.embedded.end.stylus
|
||||
pop: true
|
||||
- include: single-line-comment
|
||||
- match: |-
|
||||
(?x)
|
||||
(?:
|
||||
([\w$-]+)
|
||||
|
|
||||
('[^']*')
|
||||
|
|
||||
("[^"]*")
|
||||
)
|
||||
\s*
|
||||
(:)
|
||||
\s*
|
||||
captures:
|
||||
1: support.type.property-name.stylus
|
||||
2: string.quoted.single.stylus
|
||||
3: string.quoted.double.stylus
|
||||
4: punctuation.separator.key-value.stylus
|
||||
push:
|
||||
- match: '(;)|(?=\}|$)'
|
||||
captures:
|
||||
1: punctuation.terminator.statement.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
interpolation:
|
||||
- match: '\{'
|
||||
captures:
|
||||
0: punctuation.section.embedded.start.stylus
|
||||
push:
|
||||
- meta_scope: stylus.embedded.source
|
||||
- match: '\}'
|
||||
captures:
|
||||
0: punctuation.section.embedded.end.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
iteration:
|
||||
- match: (^\s*|\s+)(for)\s+(?=.*?\s+in\s+)
|
||||
captures:
|
||||
2: keyword.control.stylus
|
||||
push:
|
||||
- match: '$|\{'
|
||||
pop: true
|
||||
- include: expression
|
||||
language-constants:
|
||||
- match: \b(true|false|null)\b
|
||||
scope: constant.language.stylus
|
||||
language-keywords:
|
||||
- match: (\b|\s)(return|else)\b
|
||||
scope: keyword.control.stylus
|
||||
- match: (\b|\s)(!important|in|is defined|is a)\b
|
||||
scope: keyword.other.stylus
|
||||
- match: \barguments\b
|
||||
scope: variable.language.stylus
|
||||
language-operators:
|
||||
- match: ((?:\?|:|!|~|\+|-|(?:\*)?\*|\/|%|(\.)?\.\.|<|>|(?:=|:|\?|\+|-|\*|\/|%|<|>)?=|!=)|\b(?:in|is(?:nt)?|(?<!:)not)\b)
|
||||
scope: keyword.operator.stylus
|
||||
language-property-value-constants:
|
||||
- match: \b(absolute|all(-scroll)?|always|armenian|auto|avoid|baseline|below|bidi-override|block|bold(er)?|(border|content|padding)-box|both|bottom|break-all|break-word|capitalize|center|char|circle|cjk-ideographic|col-resize|collapse|crosshair|cursive|dashed|decimal-leading-zero|decimal|default|disabled|disc|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ellipsis|fantasy|fixed|geometricPrecision|georgian|groove|hand|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|inactive|inherit|inline-block|inline|inset|inside|inter-ideograph|inter-word|italic|justify|katakana-iroha|katakana|keep-all|left|lighter|line-edge|line-through|line|list-item|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|medium|middle|move|monospace|n-resize|ne-resize|newspaper|no-drop|no-repeat|nw-resize|none|normal|not-allowed|nowrap|oblique|optimize(Legibility|Quality|Speed)|outset|outside|overline|pointer|pre(-(wrap|line))?|progress|relative|repeat-x|repeat-y|repeat|right|ridge|row-resize|rtl|(sans-)?serif|s-resize|scroll|se-resize|separate|small-caps|solid|square|static|strict|sub|super|sw-resize|table(-(row|cell|footer-group|header-group))?|tb-rl|text-bottom|text-top|text|thick|thin|top|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|vertical(-(ideographic|text))?|visible(Painted|Fill|Stroke)?|w-resize|wait|whitespace|zero|smaller|larger|((xx?-)?(small(er)?|large(r)?))|painted|fill|stroke)\b
|
||||
scope: constant.property-value.stylus
|
||||
media-query:
|
||||
- match: '\s*(?![{;]|$)'
|
||||
push:
|
||||
- meta_scope: meta.at-rule.media.stylus
|
||||
- match: '\s*(?=[{;]|$)'
|
||||
pop: true
|
||||
- match: '(?i)\s*(only|not)?\s*(all|aural|braille|embossed|handheld|print|projection|screen|tty|tv)?(?![\w\d$-]+)'
|
||||
captures:
|
||||
1: keyword.operator.logic.media.stylus
|
||||
2: support.constant.media.stylus
|
||||
push:
|
||||
- match: '\s*(?:(,)|(?=[{;]|$))'
|
||||
pop: true
|
||||
- include: media-query-list
|
||||
- include: variable
|
||||
media-query-list:
|
||||
- match: \s*(and)?\s*(\()\s*
|
||||
captures:
|
||||
1: keyword.operator.logic.media.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: media-query-properties
|
||||
- include: numeric-values
|
||||
media-query-properties:
|
||||
- match: \s*:\s*
|
||||
captures:
|
||||
0: punctuation.separator.key-value.stylus
|
||||
- match: |-
|
||||
(?x)
|
||||
(
|
||||
((min|max)-)?
|
||||
(
|
||||
((device-)?(height|width|aspect-ratio))|
|
||||
(color(-index)?)|monochrome|resolution
|
||||
)
|
||||
)|grid|scan|orientation
|
||||
captures:
|
||||
0: support.type.property-name.media.stylus
|
||||
- match: \b(portrait|landscape|progressive|interlace)\b
|
||||
captures:
|
||||
1: support.constant.property-value.stylus
|
||||
numeric-values:
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(\#)(?:
|
||||
([0-9a-fA-F])
|
||||
([0-9a-fA-F])
|
||||
([0-9a-fA-F])
|
||||
([0-9a-fA-F])?
|
||||
| ([0-9a-fA-F]{2})
|
||||
([0-9a-fA-F]{2})
|
||||
([0-9a-fA-F]{2})
|
||||
([0-9a-fA-F]{2})?
|
||||
)\b
|
||||
scope: constant.other.color.rgb-value.stylus
|
||||
captures:
|
||||
1: punctuation.definition.constant.stylus
|
||||
2: constant.other.color.rgb-value.red.stylus
|
||||
3: constant.other.color.rgb-value.green.stylus
|
||||
4: constant.other.color.rgb-value.blue.stylus
|
||||
5: constant.other.color.rgb-value.alpha.stylus
|
||||
6: constant.other.color.rgb-value.red.stylus
|
||||
7: constant.other.color.rgb-value.green.stylus
|
||||
8: constant.other.color.rgb-value.blue.stylus
|
||||
9: constant.other.color.rgb-value.alpha.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
(?:-|\+)? # negative / positive
|
||||
(?:
|
||||
(?:
|
||||
[0-9]+ # integer part
|
||||
(?:\.[0-9]+)? # fraction
|
||||
) |
|
||||
(?:\.[0-9]+) # fraction without leading zero
|
||||
)
|
||||
((?: # units
|
||||
px|pt|ch|cm|mm|in|
|
||||
r?em|ex|pc|vw|vh|vmin|vmax|deg|
|
||||
g?rad|turn|dpi|dpcm|dppx|m?s|k?Hz
|
||||
)\b|%)?
|
||||
scope: constant.numeric.stylus
|
||||
captures:
|
||||
1: keyword.other.unit.stylus
|
||||
property-reference:
|
||||
- match: "@[a-z-]+"
|
||||
scope: variable.other.property.stylus
|
||||
pseudo:
|
||||
- match: (:)(active|checked|default|disabled|empty|enabled|first-child|first-of-type|first|fullscreen|focus|hover|indeterminate|in-range|invalid|last-child|last-of-type|left|link|only-child|only-of-type|optional|out-of-range|read-only|read-write|required|right|root|scope|target|valid|visited)\b
|
||||
scope: entity.other.attribute-name.pseudo-class.stylus
|
||||
captures:
|
||||
1: puncutation.definition.entity.stylus
|
||||
- match: (:?:)(before|after)\b
|
||||
scope: entity.other.attribute-name.pseudo-element.stylus
|
||||
captures:
|
||||
1: puncutation.definition.entity.stylus
|
||||
- match: (::)(first-letter|first-number|selection)\b
|
||||
scope: entity.other.attribute-name.pseudo-element.stylus
|
||||
captures:
|
||||
1: puncutation.definition.entity.stylus
|
||||
- match: ((:)dir)\s*(?:(\()(ltr|rtl)?(\)))?
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-element.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
4: constant.language.stylus
|
||||
5: puncutation.definition.parameters.end.stylus
|
||||
- match: ((:)lang)\s*(?:(\()(\w+(-\w+)?)?(\)))?
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-element.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
4: constant.language.stylus
|
||||
5: puncutation.definition.parameters.end.stylus
|
||||
- include: pseudo-nth
|
||||
- include: pseudo-not
|
||||
pseudo-not:
|
||||
- match: ((:)not)\s*(\()
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-element.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: puncutation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: selector-components
|
||||
pseudo-nth:
|
||||
- match: ((:)(?:nth-child|nth-last-child|nth-of-type|nth-last-of-type|nth-match|nth-last-match|nth-column|nth-last-column))\s*(\()
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-class.stylus
|
||||
2: puncutation.definition.entity.stylus
|
||||
3: puncutation.definition.parameters.start.stylus
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: puncutation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: language-operators
|
||||
- include: interpolation
|
||||
- match: \b(odd|even)\b
|
||||
scope: constant.language.stylus
|
||||
- match: \b(\d+)?n\b
|
||||
scope: variable.language.stylus
|
||||
captures:
|
||||
1: constant.numeric.stylus
|
||||
- match: \d+
|
||||
scope: constant.numeric.stylus
|
||||
return:
|
||||
- match: ^\s*(return)
|
||||
captures:
|
||||
1: keyword.control.stylus
|
||||
push:
|
||||
- match: (;)|(?=$)
|
||||
captures:
|
||||
1: punctuation.terminator.statement.stylus
|
||||
pop: true
|
||||
- include: expression
|
||||
selector-components:
|
||||
- include: comments
|
||||
- include: interpolation
|
||||
- include: attribute-selector
|
||||
- include: pseudo
|
||||
- match: '\$[\w$-]+\b'
|
||||
scope: entity.other.placeholder.stylus
|
||||
- match: "[:~>]"
|
||||
scope: keyword.operator.selector.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
\b(
|
||||
altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|
|
||||
animateMotion|animateTransform|circle|clipPath|color-profile|
|
||||
defs|desc|ellipse|feBlend|feColorMatrix|
|
||||
feComponentTransfer|feComposite|feConvolveMatrix|
|
||||
feDiffuseLighting|feDisplacementMap|feDistantLight|feFlood|
|
||||
feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|
|
||||
feMergeNode|feMorphology|feOffset|fePointLight|
|
||||
feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|
|
||||
font-face|font-face-format|font-face-name|font-face-src|
|
||||
font-face-uri|foreignObject|g|glyph|glyphRef|hkern|image|line|
|
||||
linearGradient|marker|mask|metadata|missing-glyph|mpath|path|
|
||||
pattern|polygon|polyline|radialGradient|rect|set|stop|svg|
|
||||
switch|symbol|text|textPath|tref|tspan|use|view|vkern|
|
||||
a|abbr|acronym|address|applet|area|article|aside|audio|b|base|
|
||||
basefont|bdi|bdo|bgsound|big|blink|blockquote|body|br|button|
|
||||
canvas|caption|center|cite|code|col|colgroup|content|data|
|
||||
datalist|dd|decorator|del|details|dfn|dir|div|dl|dt|element|
|
||||
em|embed|fieldset|figcaption|figure|font|footer|form|frame|
|
||||
frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|
|
||||
img|input|ins|isindex|kbd|keygen|label|legend|li|link|listing|
|
||||
main|map|mark|marquee|menu|menuitem|meta|meter|nav|nobr|
|
||||
noframes|noscript|object|ol|optgroup|option|output|p|param|
|
||||
plaintext|pre|progress|q|rp|rt|ruby|s|samp|script|section|
|
||||
select|shadow|small|source|spacer|span|strike|strong|style|
|
||||
sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|
|
||||
thead|time|title|tr|track|tt|u|ul|var|video|wbr|xmp
|
||||
)\b
|
||||
scope: entity.name.tag.stylus
|
||||
- match: '\.[a-zA-Z0-9_-]+'
|
||||
scope: entity.other.attribute-name.class.stylus
|
||||
- match: "#[a-zA-Z0-9_-]+"
|
||||
scope: entity.other.attribute-name.id.stylus
|
||||
- match: |-
|
||||
(?x) # multi-line regex definition mode
|
||||
([\w\d_-]+)? # matching any word right before &
|
||||
(&) # & itself, escaped because of plist
|
||||
([\w\d_-]+)? # matching any word right after &
|
||||
captures:
|
||||
1: entity.other.attribute-name.stylus
|
||||
2: variable.language.stylus
|
||||
3: entity.other.attribute-name.stylus
|
||||
single-line-comment:
|
||||
- match: (\/\/).*$
|
||||
scope: comment.line.stylus
|
||||
captures:
|
||||
1: punctuation.definition.comment.stylus
|
||||
string-quoted:
|
||||
- match: "'[^']*'"
|
||||
scope: string.quoted.single.stylus
|
||||
- match: '"[^"]*"'
|
||||
scope: string.quoted.double.stylus
|
||||
url:
|
||||
- match: (url)\s*(\()
|
||||
captures:
|
||||
1: entity.function-name.stylus
|
||||
2: punctuation.definition.parameters.start.stylus
|
||||
push:
|
||||
- meta_scope: meta.function-call.stylus
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end.stylus
|
||||
pop: true
|
||||
- include: string-quoted
|
||||
- include: language-constants
|
||||
- include: language-property-value-constants
|
||||
- include: property-reference
|
||||
- include: variable
|
||||
variable:
|
||||
- match: '([\w$-]+\b)'
|
||||
scope: variable.other.stylus
|
1
sublime/syntaxes/Sublime-GenericConfig
Submodule
1
sublime/syntaxes/Sublime-GenericConfig
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 926b6818067c741d1d5cd9bfe901954fc23eb049
|
1
sublime/syntaxes/SublimeElmLanguageSupport
Submodule
1
sublime/syntaxes/SublimeElmLanguageSupport
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit e266d279c8074aa342f106554cfa87ebe839a782
|
1
sublime/syntaxes/SublimeFortran
Submodule
1
sublime/syntaxes/SublimeFortran
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit dcf4f24f1cecd9eebf6b1eb388e4f5db671a7b08
|
1
sublime/syntaxes/SublimeSass
Submodule
1
sublime/syntaxes/SublimeSass
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit b98a3f3ccff0134c38544d9bc41caf7f61048cdf
|
1
sublime/syntaxes/SublimeTextLinkerSyntax
Submodule
1
sublime/syntaxes/SublimeTextLinkerSyntax
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 041d15667eca429afd4ff3df3b8f8617a66fc410
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue