commit
c165c17c2f
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -7,9 +7,6 @@
|
|||
[submodule "sublime_syntaxes/LESS-sublime"]
|
||||
path = sublime_syntaxes/LESS-sublime
|
||||
url = https://github.com/danro/LESS-sublime.git
|
||||
[submodule "sublime_syntaxes/TypeScript-Sublime-Plugin"]
|
||||
path = sublime_syntaxes/TypeScript-Sublime-Plugin
|
||||
url = https://github.com/Microsoft/TypeScript-Sublime-Plugin.git
|
||||
[submodule "sublime_syntaxes/Handlebars"]
|
||||
path = sublime_syntaxes/Handlebars
|
||||
url = https://github.com/daaain/Handlebars.git
|
||||
|
@ -31,3 +28,6 @@
|
|||
[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
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
dist: trusty
|
||||
language: rust
|
||||
services: docker
|
||||
sudo: required
|
||||
|
||||
env:
|
||||
global:
|
||||
|
@ -20,6 +19,9 @@ matrix:
|
|||
rust: beta
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
rust: nightly
|
||||
# The earliest stable Rust version that works
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
rust: 1.23.0
|
||||
|
||||
|
||||
before_install: set -e
|
||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,5 +1,26 @@
|
|||
# Changelog
|
||||
|
||||
## 0.3.0 (2017-01-25)
|
||||
|
||||
### Breaking
|
||||
- Change names of individual taxonomies to be plural (ie `tags/my-tag` instead of `tag/my-tag`)
|
||||
- Front matter now uses TOML dates rather strings: remove quotes from your date value to fix it.
|
||||
For example: `date = "2001-10-10"` becomes `date = 2001-10-10`
|
||||
- `language_code` has been renamed `default_language` in preparations of i18n support
|
||||
|
||||
### Others
|
||||
- Add `get_taxonomy_url` to retrieve the permalink of a tag/category
|
||||
- Fix bug when generating permalinks for taxonomies
|
||||
- Update to Tera 0.11
|
||||
- Better UX on first `serve` thanks to some default templates.
|
||||
- Add `output-dir` to `build` and `serve` to generate the site in a folder other than `public`
|
||||
- Add Prolog syntax highlighting and update all current syntaxes
|
||||
- Live reloading now works on shortcode template changes
|
||||
- `gutenberg serve` now reloads site on `config.toml` changes: you will need to F5 to see any changes though
|
||||
- Add a `trans` global function that will get return the translation of the given key for the given lang, defaulting
|
||||
to `config.default_language` if not given
|
||||
- `gutenberg serve` cleans after itself and deletes the output directory on CTRL+C
|
||||
|
||||
## 0.2.2 (2017-11-01)
|
||||
|
||||
- Fix shortcodes without arguments being ignored
|
||||
|
|
810
Cargo.lock
generated
810
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
13
Cargo.toml
13
Cargo.toml
|
@ -1,10 +1,10 @@
|
|||
[package]
|
||||
name = "gutenberg"
|
||||
version = "0.2.2"
|
||||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
version = "0.3.0"
|
||||
authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
description = "Static site generator"
|
||||
description = "A static site generator with everything built-in"
|
||||
homepage = "https://github.com/Keats/gutenberg"
|
||||
repository = "https://github.com/Keats/gutenberg"
|
||||
keywords = ["static", "site", "generator", "blog"]
|
||||
|
@ -24,11 +24,12 @@ term-painter = "0.2"
|
|||
# Used in init to ensure the url given as base_url is a valid one
|
||||
url = "1.5"
|
||||
# Below is for the serve cmd
|
||||
staticfile = "0.4"
|
||||
iron = "0.5"
|
||||
mount = "0.3"
|
||||
staticfile = "0.5"
|
||||
iron = "0.6"
|
||||
mount = "0.4"
|
||||
notify = "4"
|
||||
ws = "0.7"
|
||||
ctrlc = "3"
|
||||
|
||||
site = { path = "components/site" }
|
||||
errors = { path = "components/errors" }
|
||||
|
|
2
build.rs
2
build.rs
|
@ -1,7 +1,7 @@
|
|||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use clap::Shell;
|
||||
// use clap::Shell;
|
||||
|
||||
include!("src/cli.rs");
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ _arguments -s -S -C \
|
|||
_arguments -s -S -C \
|
||||
'-u+[Force the base URL to be that value (default to the one in config.toml)]' \
|
||||
'--base-url+[Force the base URL to be that value (default to the one in config.toml)]' \
|
||||
'-o+[Outputs the generated site in the given path]' \
|
||||
'--output-dir+[Outputs the generated site in the given path]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
|
@ -44,6 +46,8 @@ _arguments -s -S -C \
|
|||
'--interface+[Interface to bind on]' \
|
||||
'-p+[Which port to use]' \
|
||||
'--port+[Which port to use]' \
|
||||
'-o+[Outputs the generated site in the given path]' \
|
||||
'--output-dir+[Outputs the generated site in the given path]' \
|
||||
'-h[Prints help information]' \
|
||||
'--help[Prints help information]' \
|
||||
'-V[Prints version information]' \
|
||||
|
|
|
@ -53,11 +53,11 @@
|
|||
}
|
||||
|
||||
'_gutenberg_build' {
|
||||
$completions = @('-h', '-V', '-u', '--help', '--version', '--base-url')
|
||||
$completions = @('-h', '-V', '-u', '-o', '--help', '--version', '--base-url', '--output-dir')
|
||||
}
|
||||
|
||||
'_gutenberg_serve' {
|
||||
$completions = @('-h', '-V', '-i', '-p', '--help', '--version', '--interface', '--port')
|
||||
$completions = @('-h', '-V', '-i', '-p', '-o', '--help', '--version', '--interface', '--port', '--output-dir')
|
||||
}
|
||||
|
||||
'_gutenberg_help' {
|
||||
|
|
153
completions/gutenberg.bash
Normal file
153
completions/gutenberg.bash
Normal file
|
@ -0,0 +1,153 @@
|
|||
_gutenberg() {
|
||||
local i cur prev opts cmds
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
cmd=""
|
||||
opts=""
|
||||
|
||||
for i in ${COMP_WORDS[@]}
|
||||
do
|
||||
case "${i}" in
|
||||
gutenberg)
|
||||
cmd="gutenberg"
|
||||
;;
|
||||
|
||||
build)
|
||||
cmd+="__build"
|
||||
;;
|
||||
help)
|
||||
cmd+="__help"
|
||||
;;
|
||||
init)
|
||||
cmd+="__init"
|
||||
;;
|
||||
serve)
|
||||
cmd+="__serve"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "${cmd}" in
|
||||
gutenberg)
|
||||
opts=" -c -h -V --config --help --version init build serve help"
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
|
||||
gutenberg__build)
|
||||
opts=" -h -V -u -o --help --version --base-url --output-dir "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
--base-url)
|
||||
COMPREPLY=("<base_url>")
|
||||
return 0
|
||||
;;
|
||||
-u)
|
||||
COMPREPLY=("<base_url>")
|
||||
return 0
|
||||
;;
|
||||
--output-dir)
|
||||
COMPREPLY=("<output_dir>")
|
||||
return 0
|
||||
;;
|
||||
-o)
|
||||
COMPREPLY=("<output_dir>")
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
gutenberg__help)
|
||||
opts=" -h -V --help --version "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
gutenberg__init)
|
||||
opts=" -h -V --help --version <name> "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
gutenberg__serve)
|
||||
opts=" -h -V -i -p -o --help --version --interface --port --output-dir "
|
||||
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
case "${prev}" in
|
||||
|
||||
--interface)
|
||||
COMPREPLY=("<interface>")
|
||||
return 0
|
||||
;;
|
||||
-i)
|
||||
COMPREPLY=("<interface>")
|
||||
return 0
|
||||
;;
|
||||
--port)
|
||||
COMPREPLY=("<port>")
|
||||
return 0
|
||||
;;
|
||||
-p)
|
||||
COMPREPLY=("<port>")
|
||||
return 0
|
||||
;;
|
||||
--output-dir)
|
||||
COMPREPLY=("<output_dir>")
|
||||
return 0
|
||||
;;
|
||||
-o)
|
||||
COMPREPLY=("<output_dir>")
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
complete -F _gutenberg -o bashdefault -o default gutenberg
|
|
@ -21,10 +21,12 @@ complete -c gutenberg -n "__fish_using_command gutenberg" -f -a "help" -d 'Print
|
|||
complete -c gutenberg -n "__fish_using_command gutenberg init" -s h -l help -d 'Prints help information'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg init" -s V -l version -d 'Prints version information'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg build" -s u -l base-url -d 'Force the base URL to be that value (default to the one in config.toml)'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg build" -s o -l output-dir -d 'Outputs the generated site in the given path'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg build" -s h -l help -d 'Prints help information'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg build" -s V -l version -d 'Prints version information'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg serve" -s i -l interface -d 'Interface to bind on'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg serve" -s p -l port -d 'Which port to use'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg serve" -s o -l output-dir -d 'Outputs the generated site in the given path'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg serve" -s h -l help -d 'Prints help information'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg serve" -s V -l version -d 'Prints version information'
|
||||
complete -c gutenberg -n "__fish_using_command gutenberg help" -s h -l help -d 'Prints help information'
|
||||
|
|
|
@ -38,7 +38,7 @@ pub struct Config {
|
|||
/// Description of the site
|
||||
pub description: Option<String>,
|
||||
/// The language used in the site. Defaults to "en"
|
||||
pub language_code: Option<String>,
|
||||
pub default_language: Option<String>,
|
||||
/// Whether to generate RSS. Defaults to false
|
||||
pub generate_rss: Option<bool>,
|
||||
/// The number of articles to include in the RSS feed. Defaults to unlimited
|
||||
|
@ -50,6 +50,9 @@ pub struct Config {
|
|||
/// Whether to compile the `sass` directory and output the css files into the static folder
|
||||
pub compile_sass: Option<bool>,
|
||||
|
||||
/// Languages list and translated strings
|
||||
pub translations: Option<HashMap<String, Toml>>,
|
||||
|
||||
/// All user params set in [extra] in the config
|
||||
pub extra: Option<HashMap<String, Toml>>,
|
||||
|
||||
|
@ -74,13 +77,14 @@ impl Config {
|
|||
Err(e) => bail!(e)
|
||||
};
|
||||
|
||||
set_default!(config.language_code, "en".to_string());
|
||||
set_default!(config.default_language, "en".to_string());
|
||||
set_default!(config.highlight_code, false);
|
||||
set_default!(config.generate_rss, false);
|
||||
set_default!(config.rss_limit, 20);
|
||||
set_default!(config.generate_tags_pages, false);
|
||||
set_default!(config.generate_categories_pages, false);
|
||||
set_default!(config.compile_sass, false);
|
||||
set_default!(config.translations, HashMap::new());
|
||||
set_default!(config.extra, HashMap::new());
|
||||
|
||||
match config.highlight_theme {
|
||||
|
@ -120,6 +124,8 @@ impl Config {
|
|||
format!("{}{}{}", self.base_url, &path[1..], trailing_bit)
|
||||
} else if self.base_url.ends_with('/') {
|
||||
format!("{}{}{}", self.base_url, path, trailing_bit)
|
||||
} else if path.starts_with('/') {
|
||||
format!("{}{}{}", self.base_url, path, trailing_bit)
|
||||
} else {
|
||||
format!("{}/{}{}", self.base_url, path, trailing_bit)
|
||||
}
|
||||
|
@ -164,12 +170,13 @@ impl Default for Config {
|
|||
highlight_code: Some(true),
|
||||
highlight_theme: Some("base16-ocean-dark".to_string()),
|
||||
description: None,
|
||||
language_code: Some("en".to_string()),
|
||||
default_language: Some("en".to_string()),
|
||||
generate_rss: Some(false),
|
||||
rss_limit: Some(10_000),
|
||||
generate_tags_pages: Some(true),
|
||||
generate_categories_pages: Some(true),
|
||||
compile_sass: Some(false),
|
||||
translations: None,
|
||||
extra: None,
|
||||
build_timestamp: Some(1),
|
||||
}
|
||||
|
@ -272,6 +279,13 @@ hello = "world"
|
|||
assert_eq!(config.make_permalink("/hello"), "http://vincent.is/hello/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_make_url_with_localhost() {
|
||||
let mut config = Config::default();
|
||||
config.base_url = "http://127.0.0.1:1111".to_string();
|
||||
assert_eq!(config.make_permalink("/tags/rust"), "http://127.0.0.1:1111/tags/rust/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_merge_with_theme_data_and_preserve_config_value() {
|
||||
let config_str = r#"
|
||||
|
@ -293,4 +307,27 @@ a_value = 10
|
|||
assert_eq!(extra["hello"].as_str().unwrap(), "world".to_string());
|
||||
assert_eq!(extra["a_value"].as_integer().unwrap(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_use_language_configuration() {
|
||||
let config = r#"
|
||||
base_url = "https://remplace-par-ton-url.fr"
|
||||
default_language = "fr"
|
||||
|
||||
[translations]
|
||||
[translations.fr]
|
||||
title = "Un titre"
|
||||
|
||||
[translations.en]
|
||||
title = "A title"
|
||||
|
||||
"#;
|
||||
|
||||
let config = Config::parse(config);
|
||||
assert!(config.is_ok());
|
||||
let translations = config.unwrap().translations.unwrap();
|
||||
assert_eq!(translations["fr"]["title"].as_str().unwrap(), "Un titre");
|
||||
assert_eq!(translations["en"]["title"].as_str().unwrap(), "A title");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
tera = "0.10"
|
||||
tera = "0.11.0"
|
||||
serde = "1.0"
|
||||
slug = "0.1"
|
||||
rayon = "0.8"
|
||||
rayon = "0.9"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
config = { path = "../config" }
|
||||
|
@ -17,3 +17,4 @@ front_matter = { path = "../front_matter" }
|
|||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
toml = "0.4"
|
||||
|
|
|
@ -8,7 +8,7 @@ pub fn find_content_components<P: AsRef<Path>>(path: P) -> Vec<String> {
|
|||
let mut components = vec![];
|
||||
|
||||
for section in path.parent().unwrap().components() {
|
||||
let component = section.as_ref().to_string_lossy();
|
||||
let component = section.as_os_str().to_string_lossy();
|
||||
|
||||
if is_in_content {
|
||||
components.push(component.to_string());
|
||||
|
|
|
@ -11,6 +11,8 @@ extern crate utils;
|
|||
|
||||
#[cfg(test)]
|
||||
extern crate tempdir;
|
||||
#[cfg(test)]
|
||||
extern crate toml;
|
||||
|
||||
mod file_info;
|
||||
mod page;
|
||||
|
|
|
@ -103,7 +103,6 @@ impl Page {
|
|||
|
||||
if let Some(ref p) = page.meta.path {
|
||||
page.path = p.trim().trim_left_matches('/').to_string();
|
||||
|
||||
} else {
|
||||
page.path = if page.file.components.is_empty() {
|
||||
page.slug.clone()
|
||||
|
@ -207,7 +206,12 @@ impl ser::Serialize for Page {
|
|||
state.serialize_field("content", &self.content)?;
|
||||
state.serialize_field("title", &self.meta.title)?;
|
||||
state.serialize_field("description", &self.meta.description)?;
|
||||
state.serialize_field("date", &self.meta.date)?;
|
||||
// From a TOML datetime to a String first
|
||||
let date = match self.meta.date {
|
||||
Some(ref d) => Some(d.to_string()),
|
||||
None => None,
|
||||
};
|
||||
state.serialize_field("date", &date)?;
|
||||
state.serialize_field("slug", &self.slug)?;
|
||||
state.serialize_field("path", &self.path)?;
|
||||
state.serialize_field("components", &self.components)?;
|
||||
|
|
|
@ -98,13 +98,16 @@ pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use toml::value::Datetime;
|
||||
|
||||
use front_matter::{PageFrontMatter, SortBy};
|
||||
use page::Page;
|
||||
use super::{sort_pages, populate_previous_and_next_pages};
|
||||
|
||||
fn create_page_with_date(date: &str) -> Page {
|
||||
let mut front_matter = PageFrontMatter::default();
|
||||
front_matter.date = Some(date.to_string());
|
||||
front_matter.date = Some(Datetime::from_str(date).unwrap());
|
||||
Page::new("content/hello.md", front_matter)
|
||||
}
|
||||
|
||||
|
@ -136,9 +139,9 @@ mod tests {
|
|||
];
|
||||
let (pages, _) = sort_pages(input, SortBy::Date);
|
||||
// Should be sorted by date
|
||||
assert_eq!(pages[0].clone().meta.date.unwrap(), "2019-01-01");
|
||||
assert_eq!(pages[1].clone().meta.date.unwrap(), "2018-01-01");
|
||||
assert_eq!(pages[2].clone().meta.date.unwrap(), "2017-01-01");
|
||||
assert_eq!(pages[0].clone().meta.date.unwrap().to_string(), "2019-01-01");
|
||||
assert_eq!(pages[1].clone().meta.date.unwrap().to_string(), "2018-01-01");
|
||||
assert_eq!(pages[2].clone().meta.date.unwrap().to_string(), "2017-01-01");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
error-chain = "0.10"
|
||||
tera = "0.10"
|
||||
error-chain = "0.11"
|
||||
tera = "0.11.0"
|
||||
toml = "0.4"
|
||||
|
|
|
@ -4,13 +4,13 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
tera = "0.10"
|
||||
tera = "0.11.0"
|
||||
chrono = "0.4"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
toml = "0.4"
|
||||
regex = "0.2"
|
||||
lazy_static = "0.2"
|
||||
lazy_static = "1"
|
||||
|
||||
|
||||
errors = { path = "../errors" }
|
||||
|
|
|
@ -92,7 +92,7 @@ mod tests {
|
|||
+++
|
||||
title = "Title"
|
||||
description = "hey there"
|
||||
date = "2002/10/12"
|
||||
date = 2002-10-12
|
||||
+++
|
||||
Hello
|
||||
"#;
|
||||
|
@ -120,7 +120,7 @@ Hello
|
|||
+++
|
||||
title = "Title"
|
||||
description = "hey there"
|
||||
date = "2002/10/12"
|
||||
date = 2002-10-12
|
||||
+++"#;
|
||||
let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
|
||||
assert_eq!(content, "");
|
||||
|
@ -133,7 +133,7 @@ date = "2002/10/12"
|
|||
+++
|
||||
title = "Title"
|
||||
description = "hey there"
|
||||
date = "2002-10-02T15:00:00Z"
|
||||
date = 2002-10-02T15:00:00Z
|
||||
+++
|
||||
+++"#;
|
||||
let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
|
||||
|
@ -147,7 +147,7 @@ date = "2002-10-02T15:00:00Z"
|
|||
+++
|
||||
title = "Title"
|
||||
description = "hey there"
|
||||
date = "2002/10/12""#;
|
||||
date = 2002-10-12"#;
|
||||
let res = split_page_content(Path::new(""), content);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use toml;
|
|||
|
||||
use errors::Result;
|
||||
|
||||
|
||||
/// The front matter of every page
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PageFrontMatter {
|
||||
|
@ -14,7 +15,7 @@ pub struct PageFrontMatter {
|
|||
/// Description in <meta> that appears when linked, e.g. on twitter
|
||||
pub description: Option<String>,
|
||||
/// Date if we want to order pages (ie blog post)
|
||||
pub date: Option<String>,
|
||||
pub date: Option<toml::value::Datetime>,
|
||||
/// Whether this page is a draft and should be ignored for pagination etc
|
||||
pub draft: Option<bool>,
|
||||
/// The page slug. Will be used instead of the filename if present
|
||||
|
@ -71,17 +72,17 @@ impl PageFrontMatter {
|
|||
Ok(f)
|
||||
}
|
||||
|
||||
/// Converts the date in the front matter, which can be in 2 formats, into a NaiveDateTime
|
||||
/// Converts the TOML datetime to a Chrono naive datetime
|
||||
pub fn date(&self) -> Option<NaiveDateTime> {
|
||||
match self.date {
|
||||
Some(ref d) => {
|
||||
if d.contains('T') {
|
||||
DateTime::parse_from_rfc3339(d).ok().and_then(|s| Some(s.naive_local()))
|
||||
} else {
|
||||
NaiveDate::parse_from_str(d, "%Y-%m-%d").ok().and_then(|s| Some(s.and_hms(0,0,0)))
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
if let Some(ref d) = self.date {
|
||||
let d2 = d.to_string();
|
||||
if d2.contains('T') {
|
||||
DateTime::parse_from_rfc3339(&d2).ok().and_then(|s| Some(s.naive_local()))
|
||||
} else {
|
||||
NaiveDate::parse_from_str(&d2, "%Y-%m-%d").ok().and_then(|s| Some(s.and_hms(0, 0, 0)))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,6 +122,7 @@ impl Default for PageFrontMatter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PageFrontMatter;
|
||||
|
@ -203,9 +205,10 @@ mod tests {
|
|||
let content = r#"
|
||||
title = "Hello"
|
||||
description = "hey there"
|
||||
date = "2016-10-10""#;
|
||||
date = 2016-10-10
|
||||
"#;
|
||||
let res = PageFrontMatter::parse(content).unwrap();
|
||||
assert!(res.date().is_some());
|
||||
assert!(res.date.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -213,9 +216,10 @@ mod tests {
|
|||
let content = r#"
|
||||
title = "Hello"
|
||||
description = "hey there"
|
||||
date = "2002-10-02T15:00:00Z""#;
|
||||
date = 2002-10-02T15:00:00Z
|
||||
"#;
|
||||
let res = PageFrontMatter::parse(content).unwrap();
|
||||
assert!(res.date().is_some());
|
||||
assert!(res.date.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -223,9 +227,28 @@ mod tests {
|
|||
let content = r#"
|
||||
title = "Hello"
|
||||
description = "hey there"
|
||||
date = "2002/10/12""#;
|
||||
let res = PageFrontMatter::parse(content).unwrap();
|
||||
assert!(res.date().is_none());
|
||||
date = 2002/10/12"#;
|
||||
let res = PageFrontMatter::parse(content);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_parse_invalid_date_format() {
|
||||
let content = r#"
|
||||
title = "Hello"
|
||||
description = "hey there"
|
||||
date = 2002-14-01"#;
|
||||
let res = PageFrontMatter::parse(content);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_parse_date_as_string() {
|
||||
let content = r#"
|
||||
title = "Hello"
|
||||
description = "hey there"
|
||||
date = "2002-14-01""#;
|
||||
let res = PageFrontMatter::parse(content);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "0.2"
|
||||
syntect = { version = "1", features = ["static-onig"] }
|
||||
lazy_static = "1"
|
||||
syntect = "2"
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
tera = "0.10"
|
||||
tera = "0.11.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
tera = "0.10"
|
||||
tera = "0.11.0"
|
||||
regex = "0.2"
|
||||
lazy_static = "0.2"
|
||||
syntect = { version = "1", features = ["static-onig"] }
|
||||
lazy_static = "1"
|
||||
syntect = "2"
|
||||
pulldown-cmark = "0"
|
||||
slug = "0.1"
|
||||
serde = "1.0"
|
||||
|
|
|
@ -54,7 +54,7 @@ fn can_highlight_code_block_with_lang() {
|
|||
let res = markdown_to_html("```python\nlist.append(1)\n```", &context).unwrap();
|
||||
assert_eq!(
|
||||
res.0,
|
||||
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">list</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">.</span><span style=\"background-color:#2b303b;color:#bf616a;\">append</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">(</span><span style=\"background-color:#2b303b;color:#d08770;\">1</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">)</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">\n</span></pre>"
|
||||
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">list.</span><span style=\"background-color:#2b303b;color:#bf616a;\">append</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">(</span><span style=\"background-color:#2b303b;color:#d08770;\">1</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">)\n</span></pre>"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,13 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
tera = "0.10"
|
||||
tera = "0.11.0"
|
||||
glob = "0.2"
|
||||
walkdir = "2"
|
||||
rayon = "0.8"
|
||||
rayon = "0.9"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
sass-rs = "0.2"
|
||||
#sass-rs = { git = "https://github.com/compass-rs/sass-rs.git" }
|
||||
|
||||
errors = { path = "../errors" }
|
||||
config = { path = "../config" }
|
||||
|
|
|
@ -151,8 +151,6 @@ impl Site {
|
|||
orphans
|
||||
}
|
||||
|
||||
/// Used by tests to change the output path to a tmp dir
|
||||
#[doc(hidden)]
|
||||
pub fn set_output_path<P: AsRef<Path>>(&mut self, path: P) {
|
||||
self.output_path = path.as_ref().to_path_buf();
|
||||
}
|
||||
|
@ -219,27 +217,7 @@ impl Site {
|
|||
self.add_page(p, false)?;
|
||||
}
|
||||
|
||||
{
|
||||
// Another silly thing needed to not borrow &self in parallel and
|
||||
// make the borrow checker happy
|
||||
let permalinks = &self.permalinks;
|
||||
let tera = &self.tera;
|
||||
let config = &self.config;
|
||||
|
||||
self.pages.par_iter_mut()
|
||||
.map(|(_, page)| {
|
||||
let insert_anchor = pages_insert_anchors[&page.file.path];
|
||||
page.render_markdown(permalinks, tera, config, insert_anchor)
|
||||
})
|
||||
.fold(|| Ok(()), Result::and)
|
||||
.reduce(|| Ok(()), Result::and)?;
|
||||
|
||||
self.sections.par_iter_mut()
|
||||
.map(|(_, section)| section.render_markdown(permalinks, tera, config))
|
||||
.fold(|| Ok(()), Result::and)
|
||||
.reduce(|| Ok(()), Result::and)?;
|
||||
}
|
||||
|
||||
self.render_markdown()?;
|
||||
self.populate_sections();
|
||||
self.populate_tags_and_categories();
|
||||
|
||||
|
@ -248,9 +226,46 @@ impl Site {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Render the markdown of all pages/sections
|
||||
/// Used in a build and in `serve` if a shortcode has changed
|
||||
pub fn render_markdown(&mut self) -> Result<()> {
|
||||
// Another silly thing needed to not borrow &self in parallel and
|
||||
// make the borrow checker happy
|
||||
let permalinks = &self.permalinks;
|
||||
let tera = &self.tera;
|
||||
let config = &self.config;
|
||||
|
||||
// TODO: avoid the duplication with function above for that part
|
||||
// This is needed in the first place because of silly borrow checker
|
||||
let mut pages_insert_anchors = HashMap::new();
|
||||
for (_, p) in &self.pages {
|
||||
pages_insert_anchors.insert(p.file.path.clone(), self.find_parent_section_insert_anchor(&p.file.parent.clone()));
|
||||
}
|
||||
|
||||
self.pages.par_iter_mut()
|
||||
.map(|(_, page)| {
|
||||
let insert_anchor = pages_insert_anchors[&page.file.path];
|
||||
page.render_markdown(permalinks, tera, config, insert_anchor)
|
||||
})
|
||||
.fold(|| Ok(()), Result::and)
|
||||
.reduce(|| Ok(()), Result::and)?;
|
||||
|
||||
self.sections.par_iter_mut()
|
||||
.map(|(_, section)| section.render_markdown(permalinks, tera, config))
|
||||
.fold(|| Ok(()), Result::and)
|
||||
.reduce(|| Ok(()), Result::and)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_tera_global_fns(&mut self) {
|
||||
self.tera.register_global_function("trans", global_fns::make_trans(self.config.clone()));
|
||||
self.tera.register_global_function("get_page", global_fns::make_get_page(&self.pages));
|
||||
self.tera.register_global_function("get_section", global_fns::make_get_section(&self.sections));
|
||||
self.tera.register_global_function(
|
||||
"get_taxonomy_url",
|
||||
global_fns::make_get_taxonomy_url(self.tags.clone(), self.categories.clone())
|
||||
);
|
||||
self.tera.register_global_function(
|
||||
"get_url",
|
||||
global_fns::make_get_url(self.permalinks.clone(), self.config.clone())
|
||||
|
@ -318,6 +333,8 @@ impl Site {
|
|||
section.ignored_pages = vec![];
|
||||
}
|
||||
|
||||
// TODO: use references instead of cloning to avoid having to call populate_section on
|
||||
// content change
|
||||
for page in self.pages.values() {
|
||||
let parent_section_path = page.file.parent.join("_index.md");
|
||||
if self.sections.contains_key(&parent_section_path) {
|
||||
|
@ -443,7 +460,7 @@ impl Site {
|
|||
pub fn clean(&self) -> Result<()> {
|
||||
if self.output_path.exists() {
|
||||
// Delete current `public` directory so we can start fresh
|
||||
remove_dir_all(&self.output_path).chain_err(|| "Couldn't delete `public` directory")?;
|
||||
remove_dir_all(&self.output_path).chain_err(|| "Couldn't delete output directory")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -620,7 +637,13 @@ impl Site {
|
|||
&self.pages
|
||||
.values()
|
||||
.filter(|p| !p.is_draft())
|
||||
.map(|p| SitemapEntry::new(p.permalink.clone(), p.meta.date.clone()))
|
||||
.map(|p| {
|
||||
let date = match p.meta.date {
|
||||
Some(ref d) => Some(d.to_string()),
|
||||
None => None,
|
||||
};
|
||||
SitemapEntry::new(p.permalink.clone(), date)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
context.add(
|
||||
|
@ -678,7 +701,7 @@ impl Site {
|
|||
}
|
||||
|
||||
let (sorted_pages, _) = sort_pages(pages, SortBy::Date);
|
||||
context.add("last_build_date", &sorted_pages[0].meta.date);
|
||||
context.add("last_build_date", &sorted_pages[0].meta.date.clone().map(|d| d.to_string()));
|
||||
// limit to the last n elements)
|
||||
context.add("pages", &sorted_pages.iter().take(self.config.rss_limit.unwrap()).collect::<Vec<_>>());
|
||||
context.add("config", &self.config);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
+++
|
||||
title = "A draft"
|
||||
draft = true
|
||||
date = "2016-03-01"
|
||||
date = 2016-03-01
|
||||
+++
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title = "Fixed slug"
|
||||
description = ""
|
||||
slug = "something-else"
|
||||
date = "2017-01-01"
|
||||
date = 2017-01-01
|
||||
aliases = ["/an-old-url/old-page"]
|
||||
+++
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title = "Fixed URL"
|
||||
description = ""
|
||||
path = "a-fixed-url"
|
||||
date = "2017-02-01"
|
||||
date = 2017-02-01
|
||||
+++
|
||||
|
||||
A simple page with fixed url
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Python in posts"
|
||||
description = ""
|
||||
date = "2017-03-01"
|
||||
date = 2017-03-01
|
||||
+++
|
||||
|
||||
Same filename but different path
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Simple article with shortcodes"
|
||||
description = ""
|
||||
date = "2017-04-01"
|
||||
date = 2017-04-01
|
||||
+++
|
||||
|
||||
A simple page
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Docker"
|
||||
order = 1
|
||||
date = "2017-01-01"
|
||||
date = 2017-01-01
|
||||
+++
|
||||
|
||||
A simple page
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Nix"
|
||||
order = 2
|
||||
date = "2017-01-01"
|
||||
date = 2017-01-01
|
||||
+++
|
||||
|
||||
A simple page
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Python tutorial"
|
||||
order = 1
|
||||
date = "2017-01-01"
|
||||
date = 2017-01-01
|
||||
+++
|
||||
|
||||
A simple page
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
+++
|
||||
title = "Rust"
|
||||
order = 2
|
||||
date = "2017-01-01"
|
||||
date = 2017-01-01
|
||||
+++
|
||||
|
||||
A simple page
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ config.language_code }}">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ config.language_code }}">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
tera = "0.10"
|
||||
tera = "0.11.0"
|
||||
slug = "0.1"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
|
|
@ -44,7 +44,7 @@ impl TaxonomyItem {
|
|||
let (mut pages, ignored_pages) = sort_pages(pages, SortBy::Date);
|
||||
let slug = slugify(name);
|
||||
let permalink = {
|
||||
let kind_path = if kind == TaxonomyKind::Tags { "tag" } else { "category" };
|
||||
let kind_path = if kind == TaxonomyKind::Tags { "tags" } else { "categories" };
|
||||
config.make_permalink(&format!("/{}/{}", kind_path, slug))
|
||||
};
|
||||
|
||||
|
@ -188,27 +188,27 @@ mod tests {
|
|||
|
||||
assert_eq!(tags.items[0].name, "db");
|
||||
assert_eq!(tags.items[0].slug, "db");
|
||||
assert_eq!(tags.items[0].permalink, "http://a-website.com/tag/db/");
|
||||
assert_eq!(tags.items[0].permalink, "http://a-website.com/tags/db/");
|
||||
assert_eq!(tags.items[0].pages.len(), 1);
|
||||
|
||||
assert_eq!(tags.items[1].name, "js");
|
||||
assert_eq!(tags.items[1].slug, "js");
|
||||
assert_eq!(tags.items[1].permalink, "http://a-website.com/tag/js/");
|
||||
assert_eq!(tags.items[1].permalink, "http://a-website.com/tags/js/");
|
||||
assert_eq!(tags.items[1].pages.len(), 2);
|
||||
|
||||
assert_eq!(tags.items[2].name, "rust");
|
||||
assert_eq!(tags.items[2].slug, "rust");
|
||||
assert_eq!(tags.items[2].permalink, "http://a-website.com/tag/rust/");
|
||||
assert_eq!(tags.items[2].permalink, "http://a-website.com/tags/rust/");
|
||||
assert_eq!(tags.items[2].pages.len(), 2);
|
||||
|
||||
assert_eq!(categories.items[0].name, "Other");
|
||||
assert_eq!(categories.items[0].slug, "other");
|
||||
assert_eq!(categories.items[0].permalink, "http://a-website.com/category/other/");
|
||||
assert_eq!(categories.items[0].permalink, "http://a-website.com/categories/other/");
|
||||
assert_eq!(categories.items[0].pages.len(), 1);
|
||||
|
||||
assert_eq!(categories.items[1].name, "Programming tutorials");
|
||||
assert_eq!(categories.items[1].slug, "programming-tutorials");
|
||||
assert_eq!(categories.items[1].permalink, "http://a-website.com/category/programming-tutorials/");
|
||||
assert_eq!(categories.items[1].permalink, "http://a-website.com/categories/programming-tutorials/");
|
||||
assert_eq!(categories.items[1].pages.len(), 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ version = "0.1.0"
|
|||
authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
||||
|
||||
[dependencies]
|
||||
tera = "0.10"
|
||||
base64 = "0.7"
|
||||
lazy_static = "0.2"
|
||||
tera = "0.11.0"
|
||||
base64 = "0.9"
|
||||
lazy_static = "1"
|
||||
pulldown-cmark = "0"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
utils = { path = "../utils" }
|
||||
content = { path = "../content" }
|
||||
config = { path = "../config" }
|
||||
taxonomies = { path = "../taxonomies" }
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<link>{{ config.base_url }}</link>
|
||||
<description>{{ config.description }}</description>
|
||||
<generator>Gutenberg</generator>
|
||||
<language>{{ config.language_code }}</language>
|
||||
<language>{{ config.default_language }}</language>
|
||||
<atom:link href="{{ feed_url }}" 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 %}
|
||||
|
|
|
@ -6,6 +6,34 @@ use tera::{GlobalFn, Value, from_value, to_value, Result};
|
|||
use content::{Page, Section};
|
||||
use config::Config;
|
||||
use utils::site::resolve_internal_link;
|
||||
use taxonomies::Taxonomy;
|
||||
|
||||
|
||||
macro_rules! required_string_arg {
|
||||
($e: expr, $err: expr) => {
|
||||
match $e {
|
||||
Some(v) => match from_value::<String>(v.clone()) {
|
||||
Ok(u) => u,
|
||||
Err(_) => return Err($err.into())
|
||||
},
|
||||
None => return Err($err.into())
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
pub fn make_trans(config: Config) -> GlobalFn {
|
||||
let translations_config = config.translations.unwrap();
|
||||
let default_lang = to_value(config.default_language.unwrap()).unwrap();
|
||||
|
||||
Box::new(move |args| -> Result<Value> {
|
||||
let key = required_string_arg!(args.get("key"), "`trans` requires a `key` argument.");
|
||||
let lang_arg = args.get("lang").unwrap_or(&default_lang).clone();
|
||||
let lang = from_value::<String>(lang_arg).unwrap();
|
||||
let translations = &translations_config[lang.as_str()];
|
||||
Ok(to_value(&translations[key.as_str()]).unwrap())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn make_get_page(all_pages: &HashMap<PathBuf, Page>) -> GlobalFn {
|
||||
|
@ -15,17 +43,10 @@ pub fn make_get_page(all_pages: &HashMap<PathBuf, Page>) -> GlobalFn {
|
|||
}
|
||||
|
||||
Box::new(move |args| -> Result<Value> {
|
||||
match args.get("path") {
|
||||
Some(val) => match from_value::<String>(val.clone()) {
|
||||
Ok(v) => {
|
||||
match pages.get(&v) {
|
||||
Some(p) => Ok(to_value(p).unwrap()),
|
||||
None => Err(format!("Page `{}` not found.", v).into())
|
||||
}
|
||||
},
|
||||
Err(_) => Err(format!("`get_page` received path={:?} but it requires a string", val).into()),
|
||||
},
|
||||
None => Err("`get_page` requires a `path` argument.".into()),
|
||||
let path = required_string_arg!(args.get("path"), "`get_page` requires a `path` argument with a string value");
|
||||
match pages.get(&path) {
|
||||
Some(p) => Ok(to_value(p).unwrap()),
|
||||
None => Err(format!("Page `{}` not found.", path).into())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -37,17 +58,10 @@ pub fn make_get_section(all_sections: &HashMap<PathBuf, Section>) -> GlobalFn {
|
|||
}
|
||||
|
||||
Box::new(move |args| -> Result<Value> {
|
||||
match args.get("path") {
|
||||
Some(val) => match from_value::<String>(val.clone()) {
|
||||
Ok(v) => {
|
||||
match sections.get(&v) {
|
||||
Some(p) => Ok(to_value(p).unwrap()),
|
||||
None => Err(format!("Section `{}` not found.", v).into())
|
||||
}
|
||||
},
|
||||
Err(_) => Err(format!("`get_section` received path={:?} but it requires a string", val).into()),
|
||||
},
|
||||
None => Err("`get_section` requires a `path` argument.".into()),
|
||||
let path = required_string_arg!(args.get("path"), "`get_section` requires a `path` argument with a string value");
|
||||
match sections.get(&path) {
|
||||
Some(p) => Ok(to_value(p).unwrap()),
|
||||
None => Err(format!("Section `{}` not found.", path).into())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -60,40 +74,66 @@ pub fn make_get_url(permalinks: HashMap<String, String>, config: Config) -> Glob
|
|||
from_value::<bool>(c.clone()).unwrap_or(false)
|
||||
});
|
||||
|
||||
match args.get("path") {
|
||||
Some(val) => match from_value::<String>(val.clone()) {
|
||||
Ok(v) => {
|
||||
// Internal link
|
||||
if v.starts_with("./") {
|
||||
match resolve_internal_link(&v, &permalinks) {
|
||||
Ok(url) => Ok(to_value(url).unwrap()),
|
||||
Err(_) => Err(format!("Could not resolve URL for link `{}` not found.", v).into())
|
||||
}
|
||||
} else {
|
||||
// anything else
|
||||
let mut permalink = config.make_permalink(&v);
|
||||
if cachebust {
|
||||
permalink = format!("{}?t={}", permalink, config.build_timestamp.unwrap());
|
||||
}
|
||||
Ok(to_value(permalink).unwrap())
|
||||
}
|
||||
},
|
||||
Err(_) => Err(format!("`get_url` received path={:?} but it requires a string", val).into()),
|
||||
},
|
||||
None => Err("`get_url` requires a `path` argument.".into()),
|
||||
let trailing_slash = args
|
||||
.get("trailing_slash")
|
||||
.map_or(true, |c| {
|
||||
from_value::<bool>(c.clone()).unwrap_or(true)
|
||||
});
|
||||
|
||||
let path = required_string_arg!(args.get("path"), "`get_url` requires a `path` argument with a string value");
|
||||
if path.starts_with("./") {
|
||||
match resolve_internal_link(&path, &permalinks) {
|
||||
Ok(url) => Ok(to_value(url).unwrap()),
|
||||
Err(_) => Err(format!("Could not resolve URL for link `{}` not found.", path).into())
|
||||
}
|
||||
} else {
|
||||
// anything else
|
||||
let mut permalink = config.make_permalink(&path);
|
||||
if !trailing_slash && permalink.ends_with("/") {
|
||||
permalink.pop(); // Removes the slash
|
||||
}
|
||||
|
||||
if cachebust {
|
||||
permalink = format!("{}?t={}", permalink, config.build_timestamp.unwrap());
|
||||
}
|
||||
Ok(to_value(permalink).unwrap())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_get_taxonomy_url(tags: Option<Taxonomy>, categories: Option<Taxonomy>) -> GlobalFn {
|
||||
Box::new(move |args| -> Result<Value> {
|
||||
let kind = required_string_arg!(args.get("kind"), "`get_taxonomy_url` requires a `kind` argument with a string value");
|
||||
let name = required_string_arg!(args.get("name"), "`get_taxonomy_url` requires a `name` argument with a string value");
|
||||
let container = match kind.as_ref() {
|
||||
"tag" => &tags,
|
||||
"category" => &categories,
|
||||
_ => return Err("`get_taxonomy_url` can only get `tag` or `category` for the `kind` argument".into()),
|
||||
};
|
||||
|
||||
if let Some(ref c) = *container {
|
||||
for item in &c.items {
|
||||
if item.name == name {
|
||||
return Ok(to_value(item.permalink.clone()).unwrap());
|
||||
}
|
||||
}
|
||||
bail!("`get_taxonomy_url`: couldn't find `{}` in `{}` taxonomy", name, kind);
|
||||
} else {
|
||||
bail!("`get_taxonomy_url` tried to get a taxonomy of kind `{}` but there isn't any", kind);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::make_get_url;
|
||||
use super::{make_get_url, make_get_taxonomy_url, make_trans};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tera::to_value;
|
||||
|
||||
use config::Config;
|
||||
use taxonomies::{Taxonomy, TaxonomyKind, TaxonomyItem};
|
||||
|
||||
|
||||
#[test]
|
||||
|
@ -106,6 +146,27 @@ mod tests {
|
|||
assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/?t=1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_remove_trailing_slashes() {
|
||||
let config = Config::default();
|
||||
let static_fn = make_get_url(HashMap::new(), config);
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("app.css").unwrap());
|
||||
args.insert("trailing_slash".to_string(), to_value(false).unwrap());
|
||||
assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_remove_slashes_and_cachebust() {
|
||||
let config = Config::default();
|
||||
let static_fn = make_get_url(HashMap::new(), config);
|
||||
let mut args = HashMap::new();
|
||||
args.insert("path".to_string(), to_value("app.css").unwrap());
|
||||
args.insert("trailing_slash".to_string(), to_value(false).unwrap());
|
||||
args.insert("cachebust".to_string(), to_value(true).unwrap());
|
||||
assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css?t=1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_link_to_some_static_file() {
|
||||
let config = Config::default();
|
||||
|
@ -114,4 +175,59 @@ mod tests {
|
|||
args.insert("path".to_string(), to_value("app.css").unwrap());
|
||||
assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_get_tag_url() {
|
||||
let tag = TaxonomyItem::new(
|
||||
"Prog amming",
|
||||
TaxonomyKind::Tags,
|
||||
&Config::default(),
|
||||
vec![],
|
||||
);
|
||||
let tags = Taxonomy {
|
||||
kind: TaxonomyKind::Tags,
|
||||
items: vec![tag],
|
||||
};
|
||||
|
||||
let static_fn = make_get_taxonomy_url(Some(tags), None);
|
||||
// can find it correctly
|
||||
let mut args = HashMap::new();
|
||||
args.insert("kind".to_string(), to_value("tag").unwrap());
|
||||
args.insert("name".to_string(), to_value("Prog amming").unwrap());
|
||||
assert_eq!(static_fn(args).unwrap(), "http://a-website.com/tags/prog-amming/");
|
||||
// and errors if it can't find it
|
||||
let mut args = HashMap::new();
|
||||
args.insert("kind".to_string(), to_value("tag").unwrap());
|
||||
args.insert("name".to_string(), to_value("random").unwrap());
|
||||
assert!(static_fn(args).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_translate_a_string() {
|
||||
let trans_config = r#"
|
||||
base_url = "https://remplace-par-ton-url.fr"
|
||||
default_language = "fr"
|
||||
|
||||
[translations]
|
||||
[translations.fr]
|
||||
title = "Un titre"
|
||||
|
||||
[translations.en]
|
||||
title = "A title"
|
||||
|
||||
"#;
|
||||
|
||||
let config = Config::parse(trans_config).unwrap();
|
||||
let static_fn = make_trans(config);
|
||||
let mut args = HashMap::new();
|
||||
|
||||
args.insert("key".to_string(), to_value("title").unwrap());
|
||||
assert_eq!(static_fn(args.clone()).unwrap(), "Un titre");
|
||||
|
||||
args.insert("lang".to_string(), to_value("en").unwrap());
|
||||
assert_eq!(static_fn(args.clone()).unwrap(), "A title");
|
||||
|
||||
args.insert("lang".to_string(), to_value("fr").unwrap());
|
||||
assert_eq!(static_fn(args.clone()).unwrap(), "Un titre");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@ extern crate tera;
|
|||
extern crate base64;
|
||||
extern crate pulldown_cmark;
|
||||
|
||||
#[macro_use]
|
||||
extern crate errors;
|
||||
extern crate utils;
|
||||
extern crate content;
|
||||
extern crate config;
|
||||
extern crate taxonomies;
|
||||
|
||||
pub mod filters;
|
||||
pub mod global_fns;
|
||||
|
|
|
@ -5,7 +5,7 @@ authors = ["Vincent Prouillet <vincent@wearewizards.io>"]
|
|||
|
||||
[dependencies]
|
||||
errors = { path = "../errors" }
|
||||
tera = "0.10"
|
||||
tera = "0.11.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
43
components/utils/src/default_tpl.html
Normal file
43
components/utils/src/default_tpl.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Gutenberg</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Welcome to Gutenberg!</h1>
|
||||
<p>
|
||||
You're seeing this page because we couldn't find a template to render.
|
||||
</p>
|
||||
<p>
|
||||
To modify this page, create a <b>{{filename}}</b> file in the templates directory or
|
||||
<a href="https://www.getgutenberg.io/documentation/themes/installing-and-using-themes/" target="_blank">install a theme</a>.
|
||||
<br>
|
||||
You can find what variables are available in this template in the <a href="{{url}}" target="_blank">documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="https://www.getgutenberg.io/documentation/getting-started/cli-usage/" target="_blank">Get started with Gutenberg</a>
|
||||
</footer>
|
||||
<style>
|
||||
html {
|
||||
line-height: 1.5;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.container {
|
||||
font-family: "sans-serif";
|
||||
text-align: center;
|
||||
margin-top: 20vh;
|
||||
padding: 2rem;
|
||||
background: #e9e9e9;
|
||||
}
|
||||
footer {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
|
@ -2,6 +2,20 @@ use tera::{Tera, Context};
|
|||
|
||||
use errors::Result;
|
||||
|
||||
static DEFAULT_TPL: &str = include_str!("default_tpl.html");
|
||||
|
||||
|
||||
macro_rules! render_default_tpl {
|
||||
($filename: expr, $url: expr) => {
|
||||
{
|
||||
let mut context = Context::new();
|
||||
context.add("filename", $filename);
|
||||
context.add("url", $url);
|
||||
Tera::one_off(DEFAULT_TPL, &context, true).map_err(|e| e.into())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Renders the given template with the given context, but also ensures that, if the default file
|
||||
/// is not found, it will look up for the equivalent template for the current theme if there is one.
|
||||
/// Lastly, if it's a default template (index, section or page), it will just return an empty string
|
||||
|
@ -19,11 +33,19 @@ pub fn render_template(name: &str, tera: &Tera, context: &Context, theme: Option
|
|||
.map_err(|e| e.into());
|
||||
}
|
||||
|
||||
if name == "index.html" || name == "section.html" || name == "page.html" {
|
||||
return Ok(String::new());
|
||||
// maybe it's a default one?
|
||||
match name {
|
||||
"index.html" | "section.html" => {
|
||||
render_default_tpl!(name, "https://www.getgutenberg.io/documentation/templates/pages-sections/#section-variables")
|
||||
},
|
||||
"page.html" => {
|
||||
render_default_tpl!(name, "https://www.getgutenberg.io/documentation/templates/pages-sections/#page-variables")
|
||||
},
|
||||
"tag.html" | "tags.html" | "category.html" | "categories.html" => {
|
||||
render_default_tpl!(name, "https://www.getgutenberg.io/documentation/templates/tags-categories/")
|
||||
},
|
||||
_ => bail!("Tried to render `{}` but the template wasn't found", name)
|
||||
}
|
||||
|
||||
bail!("Tried to render `{}` but the template wasn't found", name)
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,7 +77,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_rewrite_all_paths_of_theme() {
|
||||
let mut tera = Tera::parse("templates/*.html").unwrap();
|
||||
let mut tera = Tera::parse("test-templates/*.html").unwrap();
|
||||
rewrite_theme_paths(&mut tera, "hyde");
|
||||
// special case to make the test work: we also rename the files to
|
||||
// match the imports
|
||||
|
|
|
@ -22,6 +22,7 @@ description = ""
|
|||
|
||||
# The date of the post.
|
||||
# 2 formats are allowed: YYYY-MM-DD (2012-10-02) and RFC3339 (2002-10-02T15:00:00Z)
|
||||
# Do not wrap dates in quotes, the line below only indicates that there is no default date
|
||||
date = ""
|
||||
|
||||
# A draft page will not be present in prev/next pagination
|
||||
|
|
|
@ -32,7 +32,8 @@ Here is a full list of the supported languages and the short names you can use:
|
|||
- Jinja2 -> ["j2", "jinja2"]
|
||||
- Julia -> ["jl"]
|
||||
- Kotlin -> ["kt", "kts"]
|
||||
- LESS -> ["less"]
|
||||
- Less -> ["less", "css.less"]
|
||||
- Nim -> ["nim", "nims"]
|
||||
- ASP -> ["asa"]
|
||||
- HTML (ASP) -> ["asp"]
|
||||
- ActionScript -> ["as"]
|
||||
|
@ -57,12 +58,12 @@ Here is a full list of the supported languages and the short names you can use:
|
|||
- Java Server Page (JSP) -> ["jsp"]
|
||||
- Java -> ["java", "bsh"]
|
||||
- Java Properties -> ["properties"]
|
||||
- JSON -> ["json", "sublime-settings", "sublime-menu", "sublime-keymap", "sublime-mousemap", "sublime-theme", "sublime-build", "sublime-project", "sublime-completions", "sublime-commands", "sublime-macro"]
|
||||
- JSON -> ["json", "sublime-settings", "sublime-menu", "sublime-keymap", "sublime-mousemap", "sublime-theme", "sublime-build", "sublime-project", "sublime-completions", "sublime-commands", "sublime-macro", "sublime-color-scheme"]
|
||||
- JavaScript -> ["js", "htc"]
|
||||
- BibTeX -> ["bib"]
|
||||
- LaTeX -> ["tex", "ltx"]
|
||||
- TeX -> ["sty", "cls"]
|
||||
- Lisp -> ["lisp", "cl", "l", "mud", "el", "scm", "ss", "lsp", "fasl"]
|
||||
- Lisp -> ["lisp", "cl", "clisp", "l", "mud", "el", "scm", "ss", "lsp", "fasl"]
|
||||
- Lua -> ["lua"]
|
||||
- Makefile -> ["make", "GNUmakefile", "makefile", "Makefile", "OCamlMakefile", "mak", "mk"]
|
||||
- Markdown -> ["md", "mdown", "markdown", "markdn"]
|
||||
|
@ -89,13 +90,14 @@ Here is a full list of the supported languages and the short names you can use:
|
|||
- Rust -> ["rs"]
|
||||
- SQL -> ["sql", "ddl", "dml"]
|
||||
- Scala -> ["scala", "sbt"]
|
||||
- Bourne Again Shell (bash) -> ["sh", "bash", "zsh", "fish", ".bash_aliases", ".bash_functions", ".bash_login", ".bash_logout", ".bash_profile", ".bash_variables", ".bashrc", ".profile", ".textmate_init"]
|
||||
- Bourne Again Shell (bash) -> ["sh", "bash", "zsh", "fish", ".bash_aliases", ".bash_completions", ".bash_functions", ".bash_login", ".bash_logout", ".bash_profile", ".bash_variables", ".bashrc", ".profile", ".textmate_init"]
|
||||
- HTML (Tcl) -> ["adp"]
|
||||
- Tcl -> ["tcl"]
|
||||
- Textile -> ["textile"]
|
||||
- XML -> ["xml", "xsd", "xslt", "tld", "dtml", "rss", "opml", "svg"]
|
||||
- YAML -> ["yaml", "yml", "sublime-syntax"]
|
||||
- Generic Config -> ["cfg", "conf", "config", "ini", "pro"]
|
||||
- SWI-Prolog -> ["pro"]
|
||||
- Generic Config -> ["cfg", "conf", "config", "ini", "pro", "mak", "mk", "Doxyfile", "inputrc", ".inputrc", "dircolors", ".dircolors", "gitmodules", ".gitmodules", "gitignore", ".gitignore", "gitattributes", ".gitattributes"]
|
||||
- Linker Script -> ["ld"]
|
||||
- TOML -> ["toml", "tml"]
|
||||
- TypeScript -> ["ts"]
|
||||
|
|
|
@ -15,9 +15,9 @@ are available at the following paths:
|
|||
|
||||
```plain
|
||||
$BASE_URL/tags/
|
||||
$BASE_URL/tag/$TAG_SLUG
|
||||
$BASE_URL/tags/$TAG_SLUG
|
||||
$BASE_URL/categories/
|
||||
$BASE_URL/category/$CATEGORY_SLUG
|
||||
$BASE_URL/categories/$CATEGORY_SLUG
|
||||
```
|
||||
|
||||
It is currently not possible to change those paths or to create custom taxonomies.
|
||||
|
|
|
@ -36,6 +36,11 @@ $ gutenberg build --base-url $DEPLOY_URL
|
|||
This is useful for example when you want to deploy previews of a site to a dynamic URL, such as Netlify
|
||||
deploy previews.
|
||||
|
||||
+You can override the default output directory 'public' by passing a other value to the `output-dir` flag.
|
||||
```bash
|
||||
$ gutenberg build --output-dir $DOCUMENT_ROOT
|
||||
```
|
||||
|
||||
## serve
|
||||
|
||||
This will build and serve the site using a local server. You can also specify
|
||||
|
@ -46,6 +51,7 @@ $ gutenberg serve
|
|||
$ gutenberg serve --port 2000
|
||||
$ gutenberg serve --interface 0.0.0.0
|
||||
$ gutenberg serve --interface 0.0.0.0 --port 2000
|
||||
$ gutenberg serve --interface 0.0.0.0 --port 2000 --output-dir www/public
|
||||
```
|
||||
|
||||
The serve command will watch all your content and will provide live reload, without
|
||||
|
|
|
@ -21,7 +21,8 @@ base_url = "mywebsite.com"
|
|||
# Used in RSS by default
|
||||
title = ""
|
||||
description = ""
|
||||
language_code = "en"
|
||||
# the default language, used in RSS and coming i18n
|
||||
default_language = "en"
|
||||
|
||||
# Theme name to use
|
||||
theme = ""
|
||||
|
@ -50,6 +51,9 @@ generate_categories_pages = false
|
|||
# Whether to compile the Sass files found in the `sass` directory
|
||||
compile_sass = false
|
||||
|
||||
# Optional translation object. The key if present should be a language code
|
||||
[translations]
|
||||
|
||||
# You can put any kind of data in there and it
|
||||
# will be accessible in all templates
|
||||
[extra]
|
||||
|
|
|
@ -70,5 +70,31 @@ we want to link to the file that is located at `static/css/app.css`:
|
|||
{{ get_url(path="css/app.css") }}
|
||||
```
|
||||
|
||||
For assets it is reccommended that you pass `trailing_slash=false` to the `get_url` function. This prevents errors
|
||||
when dealing with certain hosting providers. An example is:
|
||||
|
||||
```jinja2
|
||||
{{ get_url(path="css/app.css", trailing_slash=false) }}
|
||||
```
|
||||
|
||||
In the case of non-internal links, you can also add a cachebust of the format `?t=1290192` at the end of a URL
|
||||
by passing `cachebust=true` to the `get_url` function.
|
||||
|
||||
|
||||
### `get_taxonomy_url`
|
||||
Gets the permalink for the tag or category given.
|
||||
|
||||
```jinja2
|
||||
{% set url = get_taxonomy_url(kind="category", name=page.category) %}
|
||||
```
|
||||
|
||||
The `name` will almost come from a variable but in case you want to do it manually,
|
||||
the value should be the same as the one in the front-matter, not the slugified version.
|
||||
|
||||
### `trans`
|
||||
Gets the translation of the given `key`, for the `default_language` or the `language given
|
||||
|
||||
```jinja2
|
||||
{{ trans(key="title") }}
|
||||
{{ trans(key="title", lang="fr") }}
|
||||
```
|
||||
|
|
|
@ -57,3 +57,8 @@ To be featured on this site, the theme will require two more things:
|
|||
of importance
|
||||
|
||||
A simple theme you can use as example is [Hyde](https://github.com/Keats/hyde).
|
||||
|
||||
# Caveat
|
||||
|
||||
Please note that [include paths](https://tera.netlify.com/docs/templates/#include) can only be used in used in normal templates. Theme templates should use [macro's](https://tera.netlify.com/docs/templates/#macros) instead.
|
||||
|
||||
|
|
4
docs/templates/index.html
vendored
4
docs/templates/index.html
vendored
|
@ -7,8 +7,8 @@
|
|||
<meta name="description" content="{% block description %}{{ config.description }}{% endblock description %}">
|
||||
<meta name="author" content="{{ config.extra.author }}">
|
||||
<title>{% block title %}{{ config.title }}{% endblock title %}</title>
|
||||
<link rel="stylesheet" href="{{ get_url(path="site.css") }}"/>
|
||||
<link rel="icon" href="{{ get_url(path="favicon.ico") }}">
|
||||
<link rel="stylesheet" href="{{ get_url(path="site.css", trailing_slash=false) }}"/>
|
||||
<link rel="icon" href="{{ get_url(path="favicon.ico", trailing_slash=false) }}">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
12
src/cli.rs
12
src/cli.rs
|
@ -28,6 +28,12 @@ pub fn build_cli() -> App<'static, 'static> {
|
|||
.long("base-url")
|
||||
.takes_value(true)
|
||||
.help("Force the base URL to be that value (default to the one in config.toml)"),
|
||||
Arg::with_name("output_dir")
|
||||
.short("o")
|
||||
.long("output-dir")
|
||||
.default_value("public")
|
||||
.takes_value(true)
|
||||
.help("Outputs the generated site in the given path"),
|
||||
]),
|
||||
SubCommand::with_name("serve")
|
||||
.about("Serve the site. Rebuild and reload on change automatically")
|
||||
|
@ -42,6 +48,12 @@ pub fn build_cli() -> App<'static, 'static> {
|
|||
.long("port")
|
||||
.default_value("1111")
|
||||
.help("Which port to use"),
|
||||
Arg::with_name("output_dir")
|
||||
.short("o")
|
||||
.long("output-dir")
|
||||
.default_value("public")
|
||||
.takes_value(true)
|
||||
.help("Outputs the generated site in the given path"),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ use site::Site;
|
|||
|
||||
use console;
|
||||
|
||||
pub fn build(config_file: &str, base_url: Option<&str>) -> Result<()> {
|
||||
pub fn build(config_file: &str, base_url: Option<&str>, output_dir: &str) -> Result<()> {
|
||||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?;
|
||||
site.set_output_path(output_dir);
|
||||
if let Some(b) = base_url {
|
||||
site.config.base_url = b.to_string();
|
||||
}
|
||||
|
|
|
@ -57,8 +57,7 @@ pub fn create_new_project(name: &str) -> Result<()> {
|
|||
println!();
|
||||
console::success(&format!("Done! Your site was created in {:?}", canonicalize(path).unwrap()));
|
||||
println!();
|
||||
console::info("Get started by using the built-in server: `gutenberg serve`");
|
||||
println!("There is no built-in theme so you will see a white page.");
|
||||
println!("Visit https://github.com/Keats/gutenberg for the full documentation.");
|
||||
console::info("Get started by moving into the directory and using the built-in server: `gutenberg serve`");
|
||||
println!("Visit https://www.getgutenberg.io for the full documentation.");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std::env;
|
||||
use std::fs::remove_dir_all;
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::{Instant, Duration};
|
||||
|
@ -33,6 +34,8 @@ use mount::Mount;
|
|||
use staticfile::Static;
|
||||
use notify::{Watcher, RecursiveMode, watcher};
|
||||
use ws::{WebSocket, Sender, Message};
|
||||
use ctrlc;
|
||||
|
||||
use site::Site;
|
||||
use errors::{Result, ResultExt};
|
||||
|
||||
|
@ -45,6 +48,7 @@ enum ChangeKind {
|
|||
Templates,
|
||||
StaticFiles,
|
||||
Sass,
|
||||
Config,
|
||||
}
|
||||
|
||||
// Uglified using uglifyjs
|
||||
|
@ -77,8 +81,7 @@ fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &st
|
|||
}
|
||||
}
|
||||
|
||||
pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> {
|
||||
let start = Instant::now();
|
||||
fn create_new_site(interface: &str, port: &str, output_dir: &str, config_file: &str) -> Result<(Site, String)> {
|
||||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?;
|
||||
|
||||
let address = format!("{}:{}", interface, port);
|
||||
|
@ -88,22 +91,30 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> {
|
|||
} else {
|
||||
format!("http://{}", address)
|
||||
};
|
||||
|
||||
site.set_output_path(output_dir);
|
||||
site.load()?;
|
||||
site.enable_live_reload();
|
||||
console::notify_site_size(&site);
|
||||
console::warn_about_ignored_pages(&site);
|
||||
site.build()?;
|
||||
Ok((site, address))
|
||||
}
|
||||
|
||||
pub fn serve(interface: &str, port: &str, output_dir: &str, config_file: &str) -> Result<()> {
|
||||
let start = Instant::now();
|
||||
let (mut site, address) = create_new_site(interface, port, output_dir, config_file)?;
|
||||
console::report_elapsed_time(start);
|
||||
let mut watching_static = false;
|
||||
|
||||
// Setup watchers
|
||||
let mut watching_static = false;
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();
|
||||
watcher.watch("content/", RecursiveMode::Recursive)
|
||||
.chain_err(|| "Can't watch the `content` folder. Does it exist?")?;
|
||||
watcher.watch("templates/", RecursiveMode::Recursive)
|
||||
.chain_err(|| "Can't watch the `templates` folder. Does it exist?")?;
|
||||
watcher.watch("config.toml", RecursiveMode::Recursive)
|
||||
.chain_err(|| "Can't watch the `config.toml` file. Does it exist?")?;
|
||||
|
||||
if Path::new("static").exists() {
|
||||
watching_static = true;
|
||||
|
@ -116,9 +127,9 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> {
|
|||
|
||||
let ws_address = format!("{}:{}", interface, "1112");
|
||||
|
||||
// Start a webserver that serves the `public` directory
|
||||
// Start a webserver that serves the `output_dir` directory
|
||||
let mut mount = Mount::new();
|
||||
mount.mount("/", Static::new(Path::new("public/")));
|
||||
mount.mount("/", Static::new(Path::new(output_dir)));
|
||||
mount.mount("/livereload.js", livereload_handler);
|
||||
// Starts with a _ to not trigger the unused lint
|
||||
// we need to assign to a variable otherwise it will block
|
||||
|
@ -147,7 +158,7 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> {
|
|||
|
||||
let pwd = format!("{}", env::current_dir().unwrap().display());
|
||||
|
||||
let mut watchers = vec!["content", "templates"];
|
||||
let mut watchers = vec!["content", "templates", "config.toml"];
|
||||
if watching_static {
|
||||
watchers.push("static");
|
||||
}
|
||||
|
@ -158,6 +169,12 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> {
|
|||
println!("Listening for changes in {}/{{{}}}", pwd, watchers.join(", "));
|
||||
println!("Web server is available at http://{}", address);
|
||||
println!("Press Ctrl+C to stop\n");
|
||||
// Delete the output folder on ctrl+C
|
||||
let output_path = Path::new(output_dir).to_path_buf();
|
||||
ctrlc::set_handler(move || {
|
||||
remove_dir_all(&output_path).expect("Failed to delete output directory");
|
||||
::std::process::exit(0);
|
||||
}).expect("Error setting Ctrl-C handler");
|
||||
|
||||
use notify::DebouncedEvent::*;
|
||||
|
||||
|
@ -196,6 +213,10 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> {
|
|||
console::info(&format!("-> Sass file changed {}", path.display()));
|
||||
rebuild_done_handling(&broadcaster, site.compile_sass(&site.base_path), &p);
|
||||
},
|
||||
(ChangeKind::Config, _) => {
|
||||
console::info(&format!("-> Config changed. The whole site will be reloaded. The browser needs to be refreshed to make the changes visible."));
|
||||
site = create_new_site(interface, port, output_dir, config_file).unwrap().0;
|
||||
}
|
||||
};
|
||||
console::report_elapsed_time(start);
|
||||
}
|
||||
|
@ -249,6 +270,8 @@ fn detect_change_kind(pwd: &str, path: &Path) -> (ChangeKind, String) {
|
|||
ChangeKind::StaticFiles
|
||||
} else if path_str.starts_with("/sass") {
|
||||
ChangeKind::Sass
|
||||
} else if path_str == "/config.toml" {
|
||||
ChangeKind::Config
|
||||
} else {
|
||||
unreachable!("Got a change in an unexpected path: {}", path_str)
|
||||
};
|
||||
|
@ -299,7 +322,11 @@ mod tests {
|
|||
(
|
||||
(ChangeKind::Sass, "/sass/print.scss".to_string()),
|
||||
"/home/vincent/site", Path::new("/home/vincent/site/sass/print.scss")
|
||||
)
|
||||
),
|
||||
(
|
||||
(ChangeKind::Config, "/config.toml".to_string()),
|
||||
"/home/vincent/site", Path::new("/home/vincent/site/config.toml")
|
||||
),
|
||||
];
|
||||
|
||||
for (expected, pwd, path) in test_cases {
|
||||
|
|
|
@ -8,6 +8,7 @@ extern crate mount;
|
|||
extern crate notify;
|
||||
extern crate url;
|
||||
extern crate ws;
|
||||
extern crate ctrlc;
|
||||
|
||||
extern crate site;
|
||||
#[macro_use]
|
||||
|
@ -43,7 +44,8 @@ fn main() {
|
|||
("build", Some(matches)) => {
|
||||
console::info("Building site...");
|
||||
let start = Instant::now();
|
||||
match cmd::build(config_file, matches.value_of("base_url")) {
|
||||
let output_dir = matches.value_of("output_dir").unwrap();
|
||||
match cmd::build(config_file, matches.value_of("base_url"), output_dir) {
|
||||
Ok(()) => console::report_elapsed_time(start),
|
||||
Err(e) => {
|
||||
console::unravel_errors("Failed to build the site", &e);
|
||||
|
@ -54,8 +56,9 @@ fn main() {
|
|||
("serve", Some(matches)) => {
|
||||
let interface = matches.value_of("interface").unwrap_or("127.0.0.1");
|
||||
let port = matches.value_of("port").unwrap_or("1111");
|
||||
let output_dir = matches.value_of("output_dir").unwrap();
|
||||
console::info("Building site...");
|
||||
match cmd::serve(interface, port, config_file) {
|
||||
match cmd::serve(interface, port, output_dir, config_file) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
console::unravel_errors("", &e);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::Path;
|
||||
use std::path::{Path, Component};
|
||||
|
||||
use errors::Result;
|
||||
use site::Site;
|
||||
|
@ -231,8 +231,9 @@ pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> {
|
|||
/// What happens when a template is changed
|
||||
pub fn after_template_change(site: &mut Site, path: &Path) -> Result<()> {
|
||||
site.tera.full_reload()?;
|
||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||
|
||||
match path.file_name().unwrap().to_str().unwrap() {
|
||||
match filename {
|
||||
"sitemap.xml" => site.render_sitemap(),
|
||||
"rss.xml" => site.render_rss_feed(),
|
||||
"robots.txt" => site.render_robots(),
|
||||
|
@ -247,6 +248,14 @@ pub fn after_template_change(site: &mut Site, path: &Path) -> Result<()> {
|
|||
// We can't really know what this change affects so rebuild all
|
||||
// the things
|
||||
_ => {
|
||||
// If we are updating a shortcode, re-render the markdown of all pages/site
|
||||
// because we have no clue which one needs rebuilding
|
||||
// TODO: look if there the shortcode is used in the markdown instead of re-rendering
|
||||
// everything
|
||||
if path.components().collect::<Vec<_>>().contains(&Component::Normal("shortcodes".as_ref())) {
|
||||
site.render_markdown()?;
|
||||
}
|
||||
site.populate_sections();
|
||||
site.render_sections()?;
|
||||
site.render_orphan_pages()?;
|
||||
site.render_categories()?;
|
||||
|
|
|
@ -25,7 +25,7 @@ contexts:
|
|||
captures:
|
||||
1: keyword.operator.other.elixir
|
||||
2: keyword.control.elixir
|
||||
3: punctuation.definition.parameters.elixir
|
||||
3: punctuation.section.function.elixir
|
||||
pop: true
|
||||
- include: core_syntax
|
||||
- include: core_syntax
|
||||
|
@ -37,7 +37,7 @@ contexts:
|
|||
captures:
|
||||
1: keyword.operator.other.elixir
|
||||
2: keyword.control.elixir
|
||||
3: punctuation.definition.parameters.elixir
|
||||
3: punctuation.section.function.elixir
|
||||
pop: true
|
||||
- include: core_syntax
|
||||
core_syntax:
|
||||
|
@ -78,7 +78,7 @@ contexts:
|
|||
captures:
|
||||
1: keyword.control.module.elixir
|
||||
2: entity.name.function.public.elixir
|
||||
4: punctuation.definition.parameters.elixir
|
||||
4: punctuation.section.function.elixir
|
||||
push:
|
||||
- meta_scope: meta.function.public.elixir
|
||||
- match: (\bdo:)|(\bdo\b)|(?=\s+(def|defmacro)\b)
|
||||
|
@ -100,7 +100,7 @@ contexts:
|
|||
captures:
|
||||
1: keyword.control.module.elixir
|
||||
2: entity.name.function.private.elixir
|
||||
4: punctuation.definition.parameters.elixir
|
||||
4: punctuation.section.function.elixir
|
||||
push:
|
||||
- meta_scope: meta.function.private.elixir
|
||||
- match: (\bdo:)|(\bdo\b)|(?=\s+(defp|defmacrop)\b)
|
||||
|
@ -157,18 +157,6 @@ contexts:
|
|||
pop: true
|
||||
- include: interpolated_elixir
|
||||
- include: escaped_char
|
||||
- match: (::)
|
||||
captures:
|
||||
0: punctuation.binary.elixir
|
||||
push:
|
||||
- match: (,|>>|$)
|
||||
captures:
|
||||
0: punctuation.binary.elixir
|
||||
pop: true
|
||||
- match: '\b[a-z]\w*\b'
|
||||
scope: support.type.binary.elixir
|
||||
- match: '\b(0x[0-9A-Fa-f](?>_?[0-9A-Fa-f])*|\d(?>_?\d)*(\.(?![^[:space:][:digit:]])(?>_?\d)*)?([eE][-+]?\d(?>_?\d)*)?|0b[01]+|0o[0-7]+)\b'
|
||||
scope: constant.numeric.elixir
|
||||
- match: '(?<!\.)\b(do|end|case|bc|lc|for|if|cond|unless|try|receive|fn|defmodule|defp?|defprotocol|defimpl|defrecord|defstruct|defmacrop?|defdelegate|defcallback|defmacrocallback|defexception|defoverridable|exit|after|rescue|catch|else|raise|throw|import|require|alias|use|quote|unquote|super|with)\b(?![?!:])'
|
||||
scope: keyword.control.elixir
|
||||
- match: (?<!\.)\b(and|not|or|when|xor|in)\b
|
||||
|
@ -433,6 +421,14 @@ contexts:
|
|||
the negative lookbehind prevents against matching
|
||||
p(42.tainted?)
|
||||
scope: constant.numeric.elixir
|
||||
- match: \+\+|\-\-|<\|>
|
||||
scope: keyword.operator.concatenation.elixir
|
||||
- match: \|\>|<~>|<>|<<<|>>>|~>>|<<~|~>|<~|<\|>
|
||||
scope: keyword.operator.sigils_1.elixir
|
||||
- match: "&&&|&&"
|
||||
scope: keyword.operator.sigils_2.elixir
|
||||
- match: <\-|\\\\
|
||||
scope: keyword.operator.sigils_3.elixir
|
||||
- match: "===?|!==?|<=?|>=?"
|
||||
scope: keyword.operator.comparison.elixir
|
||||
- match: (\|\|\||&&&|^^^|<<<|>>>|~~~)
|
||||
|
|
|
@ -1,200 +0,0 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: Elm
|
||||
file_extensions:
|
||||
- elm
|
||||
scope: source.elm
|
||||
contexts:
|
||||
main:
|
||||
- match: "(`)[a-zA-Z_']*?(`)"
|
||||
scope: keyword.operator.function.infix.elm
|
||||
captures:
|
||||
1: punctuation.definition.entity.elm
|
||||
2: punctuation.definition.entity.elm
|
||||
- match: \(\)
|
||||
scope: constant.language.unit.elm
|
||||
- match: ^\b((effect|port)\s+)?(module)\s+
|
||||
captures:
|
||||
1: keyword.other.elm
|
||||
3: keyword.other.elm
|
||||
push:
|
||||
- meta_scope: meta.declaration.module.elm
|
||||
- match: $|;
|
||||
captures:
|
||||
1: keyword.other.elm
|
||||
pop: true
|
||||
- include: module_name
|
||||
- match: '(where)\s*\{'
|
||||
captures:
|
||||
1: keyword.other.elm
|
||||
push:
|
||||
- match: '\}'
|
||||
pop: true
|
||||
- include: type_signature
|
||||
- match: (exposing)
|
||||
scope: keyword.other.elm
|
||||
- include: module_exports
|
||||
- match: (where)
|
||||
scope: keyword.other.elm
|
||||
- match: "[a-z]+"
|
||||
scope: invalid
|
||||
- match: ^\b(import)\s+((open)\s+)?
|
||||
captures:
|
||||
1: keyword.other.elm
|
||||
3: invalid
|
||||
push:
|
||||
- meta_scope: meta.import.elm
|
||||
- match: ($|;)
|
||||
pop: true
|
||||
- match: (as|exposing)
|
||||
scope: keyword.import.elm
|
||||
- include: module_name
|
||||
- include: module_exports
|
||||
- match: '(\[)(glsl)(\|)'
|
||||
captures:
|
||||
1: keyword.other.elm
|
||||
2: support.function.prelude.elm
|
||||
3: keyword.other.elm
|
||||
push:
|
||||
- meta_scope: entity.glsl.elm
|
||||
- match: '(\|\])'
|
||||
captures:
|
||||
1: keyword.other.elm
|
||||
pop: true
|
||||
- include: scope:source.glsl
|
||||
- match: \b(type alias|type|case|of|let|in|as)\s+
|
||||
scope: keyword.other.elm
|
||||
- match: \b(if|then|else)\s+
|
||||
scope: keyword.control.elm
|
||||
- match: '\b([0-9]+\.[0-9]+([eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+)\b'
|
||||
comment: Floats are always decimal
|
||||
scope: constant.numeric.float.elm
|
||||
- match: '\b([0-9]+)\b'
|
||||
scope: constant.numeric.elm
|
||||
- match: '"""'
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.elm
|
||||
push:
|
||||
- meta_scope: string.quoted.double.elm
|
||||
- match: '"""'
|
||||
captures:
|
||||
0: punctuation.definition.string.end.elm
|
||||
pop: true
|
||||
- match: '\\(NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|[abfnrtv\\''\&])'
|
||||
scope: constant.character.escape.elm
|
||||
- match: '\^[A-Z@\[\]\\\^_]'
|
||||
scope: constant.character.escape.control.elm
|
||||
- match: '"'
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.elm
|
||||
push:
|
||||
- meta_scope: string.quoted.double.elm
|
||||
- match: '"'
|
||||
captures:
|
||||
0: punctuation.definition.string.end.elm
|
||||
pop: true
|
||||
- match: '\\(NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|[abfnrtv\\\"''\&])'
|
||||
scope: constant.character.escape.elm
|
||||
- match: '\^[A-Z@\[\]\\\^_]'
|
||||
scope: constant.character.escape.control.elm
|
||||
- match: |-
|
||||
(?x)
|
||||
(')
|
||||
(?:
|
||||
[\ -\[\]-~] # Basic Char
|
||||
| (\\(?:NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE
|
||||
|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS
|
||||
|US|SP|DEL|[abfnrtv\\\"'\&])) # Escapes
|
||||
| (\^[A-Z@\[\]\\\^_]) # Control Chars
|
||||
)
|
||||
(')
|
||||
scope: string.quoted.single.elm
|
||||
captures:
|
||||
1: punctuation.definition.string.begin.elm
|
||||
2: constant.character.escape.elm
|
||||
3: punctuation.definition.string.end.elm
|
||||
- match: '^(port\s+)?([a-z_][a-zA-Z0-9_'']*|\([|!%$+\-.,=</>]+\))\s*((:)([:]+)?)'
|
||||
captures:
|
||||
1: keyword.other.port.elm
|
||||
2: entity.name.function.elm
|
||||
4: keyword.other.colon.elm
|
||||
5: invalid
|
||||
push:
|
||||
- meta_scope: meta.function.type-declaration.elm
|
||||
- match: $\n?
|
||||
pop: true
|
||||
- include: type_signature
|
||||
- match: \bport\s+
|
||||
scope: keyword.other.port.elm
|
||||
- match: '\b[A-Z]\w*\b'
|
||||
scope: constant.other.elm
|
||||
- include: comments
|
||||
- match: '^[a-z][A-Za-z0-9_'']*\s+'
|
||||
scope: entity.name.function.elm
|
||||
- include: infix_op
|
||||
- match: '[|!%$?~+:\-.=</>&\\*^]+'
|
||||
scope: keyword.operator.elm
|
||||
- match: '([\[\]\{\},])'
|
||||
scope: constant.language.delimiter.elm
|
||||
captures:
|
||||
1: support.function.delimiter.elm
|
||||
- match: '([\(\)])'
|
||||
scope: keyword.other.parenthesis.elm
|
||||
block_comment:
|
||||
- match: '\{-(?!#)'
|
||||
captures:
|
||||
0: punctuation.definition.comment.elm
|
||||
push:
|
||||
- meta_scope: comment.block.elm
|
||||
- include: block_comment
|
||||
- match: '-\}'
|
||||
captures:
|
||||
0: punctuation.definition.comment.elm
|
||||
pop: true
|
||||
comments:
|
||||
- match: (--).*$\n?
|
||||
scope: comment.line.double-dash.elm
|
||||
captures:
|
||||
1: punctuation.definition.comment.elm
|
||||
- include: block_comment
|
||||
infix_op:
|
||||
- match: '(\([|!%$+:\-.=</>]+\)|\(,+\))'
|
||||
scope: entity.name.function.infix.elm
|
||||
module_exports:
|
||||
- match: \(
|
||||
push:
|
||||
- meta_scope: meta.declaration.exports.elm
|
||||
- match: \)
|
||||
pop: true
|
||||
- match: '\b[a-z][a-zA-Z_''0-9]*'
|
||||
scope: entity.name.function.elm
|
||||
- match: '\b[A-Z][A-Za-z_''0-9]*'
|
||||
scope: storage.type.elm
|
||||
- match: ","
|
||||
scope: punctuation.separator.comma.elm
|
||||
- include: infix_op
|
||||
- match: \(.*?\)
|
||||
comment: So named because I don't know what to call this.
|
||||
scope: meta.other.unknown.elm
|
||||
module_name:
|
||||
- match: "[A-Z][A-Za-z._']*"
|
||||
scope: support.other.module.elm
|
||||
type_signature:
|
||||
- match: '\(\s*([A-Z][A-Za-z]*)\s+([a-z][A-Za-z_'']*)\)\s*(=>)'
|
||||
scope: meta.class-constraint.elm
|
||||
captures:
|
||||
1: entity.other.inherited-class.elm
|
||||
2: variable.other.generic-type.elm
|
||||
3: keyword.other.big-arrow.elm
|
||||
- match: "->"
|
||||
scope: keyword.other.arrow.elm
|
||||
- match: "=>"
|
||||
scope: keyword.other.big-arrow.elm
|
||||
- match: '\b[a-z][a-zA-Z0-9_'']*\b'
|
||||
scope: variable.other.generic-type.elm
|
||||
- match: '\b[A-Z][a-zA-Z0-9_'']*\b'
|
||||
scope: storage.type.elm
|
||||
- match: \(\)
|
||||
scope: support.constant.unit.elm
|
||||
- include: comments
|
|
@ -1 +1 @@
|
|||
Subproject commit 581b9e6f5bfdf55e506ac5a2b18422296c375ca2
|
||||
Subproject commit 6f2e53603a62663463f95079b308a1c9adbabeec
|
|
@ -1 +1 @@
|
|||
Subproject commit 3cf94d55b2eafd41461c45570d982eef1d65778c
|
||||
Subproject commit 581805e47c7af5ab0a880aaef5b27f8c1ccc29aa
|
|
@ -1 +1 @@
|
|||
Subproject commit ebd4a2f049fb61686664a68c8d0f34d83292e07e
|
||||
Subproject commit df5a27523dd37ebe67ba4c7d36ea162dae95b2c3
|
|
@ -1,326 +0,0 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: LESS
|
||||
comment: LESS
|
||||
file_extensions:
|
||||
- less
|
||||
scope: source.less
|
||||
contexts:
|
||||
main:
|
||||
- include: comment-block
|
||||
- include: comment-line
|
||||
- include: less-at-rules
|
||||
- include: less-declarations
|
||||
- include: less-variables
|
||||
- include: less-functions
|
||||
- include: string-double
|
||||
- include: string-single
|
||||
- include: selector
|
||||
- include: rule-list
|
||||
- include: less-operators
|
||||
- include: less-parameters
|
||||
- include: numeric-values
|
||||
color-values:
|
||||
- match: \b(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)\b
|
||||
comment: http://www.w3.org/TR/CSS21/syndata.html#value-def-color
|
||||
scope: support.constant.color.w3c-standard-color-name.css
|
||||
- match: \b(aliceblue|antiquewhite|aquamarine|azure|beige|bisque|blanchedalmond|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|gainsboro|ghostwhite|gold|goldenrod|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|limegreen|linen|magenta|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|oldlace|olivedrab|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|thistle|tomato|turquoise|violet|wheat|whitesmoke|yellowgreen)\b
|
||||
comment: "These colours are mostly recognised but will not validate. ref: http://www.w3schools.com/css/css_colornames.asp"
|
||||
scope: support.constant.color.non-standard
|
||||
- match: (hsla?|rgba?)\s*(\()
|
||||
captures:
|
||||
1: support.function.misc.css
|
||||
2: punctuation.section.function.css
|
||||
push:
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.section.function.css
|
||||
pop: true
|
||||
- match: '(?x)\b(0*((1?[0-9]{1,2})|(2([0-4][0-9]|5[0-5])))\s*,\s*){2}(0*((1?[0-9]{1,2})|(2([0-4][0-9]|5[0-5])))\b)(\s*,\s*((0?\.[0-9]+)|[0-1]))?'
|
||||
scope: constant.other.color.rgb-value.css
|
||||
- match: '\b([0-9]{1,2}|100)\s*%,\s*([0-9]{1,2}|100)\s*%,\s*([0-9]{1,2}|100)\s*%'
|
||||
scope: constant.other.color.rgb-percentage.css
|
||||
- include: numeric-values
|
||||
comment-block:
|
||||
- match: /\*
|
||||
captures:
|
||||
0: punctuation.definition.comment.css
|
||||
push:
|
||||
- meta_scope: comment.block.css
|
||||
- match: \*/
|
||||
captures:
|
||||
0: punctuation.definition.comment.css
|
||||
pop: true
|
||||
comment-line:
|
||||
- match: //
|
||||
captures:
|
||||
0: punctuation.definition.comment.css
|
||||
push:
|
||||
- meta_scope: comment.line.double-slash.less
|
||||
- match: $\n?
|
||||
captures:
|
||||
0: punctuation.definition.comment.css
|
||||
pop: true
|
||||
less-at-rules:
|
||||
- match: ^\s*((@)(?:-(?:webkit|moz|o)-)?(charset|import|namespace|media|page|font-face|keyframes|supports|document)\b)
|
||||
scope: meta.at-rule.css
|
||||
captures:
|
||||
1: keyword.control.at-rule.css
|
||||
2: punctuation.definition.keyword.css
|
||||
less-data-uri:
|
||||
- match: (url)(\()(data:)
|
||||
captures:
|
||||
1: support.function.misc.css
|
||||
2: punctuation.section.function.css
|
||||
3: parameter.less.data-uri comment markup.raw
|
||||
push:
|
||||
- meta_content_scope: parameter.less.data-uri comment markup.raw
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.section.function.css
|
||||
pop: true
|
||||
- match: (url)(\()("data:)
|
||||
captures:
|
||||
1: support.function.misc.css
|
||||
2: punctuation.section.function.css
|
||||
3: parameter.less.data-uri comment markup.raw
|
||||
push:
|
||||
- meta_content_scope: parameter.less.data-uri comment markup.raw
|
||||
- match: (?<=")(\))
|
||||
captures:
|
||||
1: punctuation.section.function.css
|
||||
pop: true
|
||||
- match: (url)(\()('data:)
|
||||
captures:
|
||||
1: support.function.misc.css
|
||||
2: punctuation.section.function.css
|
||||
3: parameter.less.data-uri comment markup.raw
|
||||
push:
|
||||
- meta_content_scope: parameter.less.data-uri comment markup.raw
|
||||
- match: (?<=\')(\))
|
||||
captures:
|
||||
1: punctuation.section.function.css
|
||||
pop: true
|
||||
less-declarations:
|
||||
- match: '(?>@[a-zA-Z0-9_-][\w-]*+)(?!:)'
|
||||
scope: variable.other.less
|
||||
- match: '@[a-zA-Z0-9_-][\w-]*'
|
||||
scope: variable.declaration.less
|
||||
less-functions:
|
||||
- match: '([%a-zA-Z\-\_\d]*)(?=\()'
|
||||
scope: support.function.less
|
||||
- match: '([\.#](?![0-9])[a-zA-Z0-9_-]+(?=\())'
|
||||
captures:
|
||||
1: entity.other.attribute-name.class.css entity.other.less.mixin
|
||||
less-operators:
|
||||
- match: /|!important|$|%|&|\*|\-\-|\-|\+\+|\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\|\||\?\:|(?<!\()/=|%=|\+=|\-=|&=|when\b|and\b|not\b
|
||||
scope: keyword.operator.less
|
||||
less-parameters:
|
||||
- match: '\(|(}\s*,)'
|
||||
push:
|
||||
- meta_scope: parameter.less
|
||||
- match: '\)|{'
|
||||
pop: true
|
||||
- include: color-values
|
||||
- include: less-parameters
|
||||
- include: less-functions
|
||||
- include: numeric-values
|
||||
- include: string-double
|
||||
- include: string-single
|
||||
- include: less-variables
|
||||
- match: '(?:\:/)?[\/\.a-zA-Z0-9_\-]*'
|
||||
scope: variable.parameter.misc.css
|
||||
- include: less-operators
|
||||
less-variables:
|
||||
- match: '@[a-zA-Z0-9_-][\w-]*'
|
||||
scope: variable.other.less
|
||||
- match: '@{[a-zA-Z0-9_-][\w-]*}'
|
||||
scope: variable.interpolation.less
|
||||
- match: "`"
|
||||
push:
|
||||
- meta_scope: comment markup.raw
|
||||
- match: "`"
|
||||
pop: true
|
||||
- match: (~)`
|
||||
captures:
|
||||
1: keyword.operator.less
|
||||
push:
|
||||
- meta_scope: comment markup.raw
|
||||
- match: "`"
|
||||
pop: true
|
||||
- match: (~)"
|
||||
captures:
|
||||
1: keyword.operator.less
|
||||
push:
|
||||
- meta_scope: string.quoted.double.css comment markup.raw
|
||||
- match: '"'
|
||||
pop: true
|
||||
- match: (~)'
|
||||
captures:
|
||||
1: keyword.operator.less
|
||||
push:
|
||||
- meta_scope: string.quoted.single.css comment markup.raw
|
||||
- match: "'"
|
||||
pop: true
|
||||
numeric-values:
|
||||
- match: '(#)([0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b'
|
||||
scope: constant.other.color.rgb-value.css
|
||||
captures:
|
||||
1: punctuation.definition.constant.css
|
||||
- match: '(?x)(?:-|\+)?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+))((?:px|pt|ch|cm|mm|in|r?em|ex|pc|deg|g?rad|dpi|dpcm|ms|s)\b|%)?'
|
||||
scope: constant.numeric.css
|
||||
captures:
|
||||
1: keyword.other.unit.css
|
||||
property-names:
|
||||
- match: "(?<![-a-z])(?=[-a-z])"
|
||||
push:
|
||||
- meta_scope: support.type.property-name.css
|
||||
- match: "$|(?![-a-z])"
|
||||
pop: true
|
||||
- match: \b(word)\b
|
||||
scope: support.type.property-name.css
|
||||
property-values:
|
||||
- include: less-variables
|
||||
- include: less-data-uri
|
||||
- include: less-functions
|
||||
- include: less-operators
|
||||
- include: color-values
|
||||
- include: numeric-values
|
||||
- include: less-parameters
|
||||
- match: \b(absolute|all(-scroll)?|always|subpixel-antialiased|antialiased|armenian|auto|avoid|baseline|below|bidi-override|block|bold|bolder|border-box|both|bottom|break-all|break-word|capitalize|center|char|circle|cjk-ideographic|content-box|col-resize|collapse|crosshair|dashed|decimal-leading-zero|decimal|default|disabled|disc|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ellipsis|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|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|s-resize|scroll|se-resize|separate|small-caps|solid|square|static|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table|tb-rl|text-bottom|text-top|textfield|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|large))|painted|fill|stroke)\b
|
||||
scope: support.constant.property-value.css
|
||||
- include: selector
|
||||
- match: (\b(?i:arial|century|comic|courier|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace)\b)
|
||||
scope: support.constant.font-name.css
|
||||
- include: string-double
|
||||
- include: string-single
|
||||
- match: (rect)\s*(\()
|
||||
captures:
|
||||
1: support.function.misc.css
|
||||
2: punctuation.section.function.css
|
||||
push:
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.section.function.css
|
||||
pop: true
|
||||
- include: numeric-values
|
||||
- match: (format|local|url|attr|counter|counters)\s*(\()
|
||||
captures:
|
||||
1: support.function.misc.css
|
||||
2: punctuation.section.function.css
|
||||
push:
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.section.function.css
|
||||
pop: true
|
||||
- include: string-single
|
||||
- include: string-double
|
||||
- match: '[^''") \t]+'
|
||||
scope: variable.parameter.misc.css
|
||||
- match: \!\s*important
|
||||
scope: keyword.other.important.css
|
||||
rule-list:
|
||||
- include: comment-block
|
||||
- include: comment-line
|
||||
- include: selector
|
||||
- include: property-names
|
||||
- match: (:)\s*
|
||||
captures:
|
||||
1: punctuation.separator.key-value.css
|
||||
push:
|
||||
- meta_scope: meta.property-value.css
|
||||
- match: '\s*(;|(?=[{}]))'
|
||||
captures:
|
||||
1: punctuation.terminator.rule.css
|
||||
pop: true
|
||||
- include: property-values
|
||||
selector:
|
||||
- match: '\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|eventsource|fieldset|figure|figcaption|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|svg|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\b'
|
||||
scope: entity.name.tag.css, keyword.control.html.elements
|
||||
- match: '(\.)-?[a-zA-Z_][a-zA-Z0-9_-]*'
|
||||
scope: entity.other.attribute-name.class.css
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
- match: "(#)-?[a-zA-Z_][a-zA-Z0-9_-]*"
|
||||
scope: entity.other.attribute-name.id.css
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
- match: \*
|
||||
scope: entity.name.tag.wildcard.css
|
||||
- match: (:+)((first|last|only)-child|(first|last|only)-of-type|empty|root|target|first-letter|first-line|first|left|right|lang)\b
|
||||
scope: entity.other.attribute-name.pseudo-class.css
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
- match: (:)(extend)\b
|
||||
scope: entity.other.attribute-name.pseudo-class.less
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
- match: (:)(checked|enabled|default|disabled|indeterminate|invalid|optional|required|valid)\b
|
||||
scope: entity.other.attribute-name.pseudo-class.ui-state.css
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
- match: ((:)not)(\()
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-class.css
|
||||
2: punctuation.definition.entity.css
|
||||
3: punctuation.section.function.css
|
||||
push:
|
||||
- match: \)
|
||||
captures:
|
||||
0: punctuation.section.function.css
|
||||
pop: true
|
||||
- include: selector
|
||||
- match: ((:)nth-(?:(?:last-)?child|(?:last-)?of-type))(\()
|
||||
captures:
|
||||
1: entity.other.attribute-name.pseudo-class.css
|
||||
2: punctuation.definition.entity.css
|
||||
3: punctuation.section.function.css
|
||||
push:
|
||||
- meta_content_scope: constant.numeric.css
|
||||
- match: (\))
|
||||
captures:
|
||||
1: punctuation.section.function.css
|
||||
pop: true
|
||||
- match: (:+)(?:-(?:webkit|moz|o)-)?(after|before|selection|scrollbar|placeholder|input-placeholder)\b
|
||||
scope: entity.other.attribute-name.pseudo-element.css
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
- match: (:+)(?:-(?:webkit|moz|o)-)?(active|hover|link|visited|focus-inner|focus)\b
|
||||
scope: entity.other.attribute-name.pseudo-class.css
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
- match: '(?i)(\[)\s*(-?[_a-z\\[[:^ascii:]]][_a-z0-9\-\\[[:^ascii:]]]*)(?:\s*([~|^$*]?=)\s*(?:(-?[_a-z\\[[:^ascii:]]][_a-z0-9\-\\[[:^ascii:]]]*)|((?>([''"])(?:[^\\]|\\.)*?(\6)))))?\s*(\])'
|
||||
scope: meta.attribute-selector.css
|
||||
captures:
|
||||
1: punctuation.definition.entity.css
|
||||
2: entity.other.attribute-name.attribute.css
|
||||
3: punctuation.separator.operator.css
|
||||
4: string.unquoted.attribute-value.css
|
||||
5: string.quoted.double.attribute-value.css
|
||||
6: punctuation.definition.string.begin.css
|
||||
7: punctuation.definition.string.end.css
|
||||
string-double:
|
||||
- match: '"'
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.css
|
||||
push:
|
||||
- meta_scope: string.quoted.double.css
|
||||
- match: '"'
|
||||
captures:
|
||||
0: punctuation.definition.string.end.css
|
||||
pop: true
|
||||
- match: \\.
|
||||
scope: constant.character.escape.css
|
||||
string-single:
|
||||
- match: "'"
|
||||
captures:
|
||||
0: punctuation.definition.string.begin.css
|
||||
push:
|
||||
- meta_scope: string.quoted.single.css
|
||||
- match: "'"
|
||||
captures:
|
||||
0: punctuation.definition.string.end.css
|
||||
pop: true
|
||||
- match: \\.
|
||||
scope: constant.character.escape.css
|
|
@ -1 +1 @@
|
|||
Subproject commit 928a7a618d99631ea424c45e74fb01d1fb6f6853
|
||||
Subproject commit ce7af4d6177d0340230893e49742070ae4143246
|
135
sublime_syntaxes/Prolog.sublime-syntax
Normal file
135
sublime_syntaxes/Prolog.sublime-syntax
Normal file
|
@ -0,0 +1,135 @@
|
|||
%YAML 1.2
|
||||
---
|
||||
# http://www.sublimetext.com/docs/3/syntax.html
|
||||
name: SWI-Prolog
|
||||
comment: This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
file_extensions:
|
||||
- pro
|
||||
scope: source.prolog
|
||||
contexts:
|
||||
main:
|
||||
- include: comments
|
||||
- match: (?<=:-)\s*
|
||||
push:
|
||||
- meta_scope: meta.clause.body.prolog
|
||||
- match: (\.)
|
||||
captures:
|
||||
1: keyword.control.clause.bodyend.prolog
|
||||
pop: true
|
||||
- include: comments
|
||||
- include: builtin
|
||||
- include: controlandkeywords
|
||||
- include: atom
|
||||
- include: variable
|
||||
- include: constants
|
||||
- match: .
|
||||
scope: meta.clause.body.prolog
|
||||
- match: '^\s*([a-z][a-zA-Z0-9_]*)(\(?)(?=.*:-.*)'
|
||||
captures:
|
||||
1: entity.name.function.clause.prolog
|
||||
2: punctuation.definition.parameters.begin
|
||||
push:
|
||||
- meta_scope: meta.clause.head.prolog
|
||||
- match: ((\)?))\s*(:-)
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end
|
||||
3: keyword.control.clause.bodybegin.prolog
|
||||
pop: true
|
||||
- include: atom
|
||||
- include: variable
|
||||
- include: constants
|
||||
- match: '^\s*([a-z][a-zA-Z0-9_]*)(\(?)(?=.*-->.*)'
|
||||
captures:
|
||||
1: entity.name.function.dcg.prolog
|
||||
2: punctuation.definition.parameters.begin
|
||||
push:
|
||||
- meta_scope: meta.dcg.head.prolog
|
||||
- match: ((\)?))\s*(-->)
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end
|
||||
3: keyword.control.dcg.bodybegin.prolog
|
||||
pop: true
|
||||
- include: atom
|
||||
- include: variable
|
||||
- include: constants
|
||||
- match: (?<=-->)\s*
|
||||
push:
|
||||
- meta_scope: meta.dcg.body.prolog
|
||||
- match: (\.)
|
||||
captures:
|
||||
1: keyword.control.dcg.bodyend.prolog
|
||||
pop: true
|
||||
- include: comments
|
||||
- include: controlandkeywords
|
||||
- include: atom
|
||||
- include: variable
|
||||
- include: constants
|
||||
- match: .
|
||||
scope: meta.dcg.body.prolog
|
||||
- match: '^\s*([a-zA-Z][a-zA-Z0-9_]*)(\(?)(?!.*(:-|-->).*)'
|
||||
captures:
|
||||
1: entity.name.function.fact.prolog
|
||||
2: punctuation.definition.parameters.begin
|
||||
push:
|
||||
- meta_scope: meta.fact.prolog
|
||||
- match: ((\)?))\s*(\.)(?!\d+)
|
||||
captures:
|
||||
1: punctuation.definition.parameters.end
|
||||
3: keyword.control.fact.end.prolog
|
||||
pop: true
|
||||
- include: atom
|
||||
- include: variable
|
||||
- include: constants
|
||||
atom:
|
||||
- match: '(?<![a-zA-Z0-9_])[a-z][a-zA-Z0-9_]*(?!\s*\(|[a-zA-Z0-9_])'
|
||||
scope: constant.other.atom.simple.prolog
|
||||
- match: "'.*?'"
|
||||
scope: constant.other.atom.quoted.prolog
|
||||
- match: '\[\]'
|
||||
scope: constant.other.atom.emptylist.prolog
|
||||
builtin:
|
||||
- match: \b(op|findall|write|nl|writeln|fail|use_module|module)\b
|
||||
scope: keyword.other
|
||||
comments:
|
||||
- match: "%.*"
|
||||
scope: comment.line.percent-sign.prolog
|
||||
- match: /\*
|
||||
captures:
|
||||
0: punctuation.definition.comment.prolog
|
||||
push:
|
||||
- meta_scope: comment.block.prolog
|
||||
- match: \*/
|
||||
captures:
|
||||
0: punctuation.definition.comment.prolog
|
||||
pop: true
|
||||
constants:
|
||||
- match: '(?<![a-zA-Z]|/)(\d+|(\d+\.\d+))'
|
||||
scope: constant.numeric.integer.prolog
|
||||
- match: '".*?"'
|
||||
scope: string.quoted.double.prolog
|
||||
controlandkeywords:
|
||||
- match: (->)
|
||||
captures:
|
||||
1: keyword.control.if.prolog
|
||||
push:
|
||||
- meta_scope: meta.if.prolog
|
||||
- match: (;)
|
||||
captures:
|
||||
1: keyword.control.else.prolog
|
||||
pop: true
|
||||
- include: main
|
||||
- include: builtin
|
||||
- include: comments
|
||||
- include: atom
|
||||
- include: variable
|
||||
- match: .
|
||||
scope: meta.if.body.prolog
|
||||
- match: "!"
|
||||
scope: keyword.control.cut.prolog
|
||||
- match: (\s(is)\s)|=:=|=?\\?=|\\\+|@?>|@?=?<|\+|\*|\-
|
||||
scope: keyword.operator.prolog
|
||||
variable:
|
||||
- match: "(?<![a-zA-Z0-9_])[A-Z][a-zA-Z0-9_]*"
|
||||
scope: variable.parameter.uppercase.prolog
|
||||
- match: (?<!\w)_
|
||||
scope: variable.language.anonymous.prolog
|
|
@ -1 +1 @@
|
|||
Subproject commit 5f12fdaae34303ab2550394a14599ad0885b26ec
|
||||
Subproject commit 04ec6d7165e971db1fefd604942b04ccf86fb920
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 79bf8ddfb8a05a2b104f3937cd91b6f2afbbb943
|
1
sublime_syntaxes/TypeScript-TmLanguage
Submodule
1
sublime_syntaxes/TypeScript-TmLanguage
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit c29d12d8aceb1a68af4cb6e466199846f41dd2ed
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue