Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions web/book/highlight-prql.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ formatting = function (hljs) {

hljs.registerLanguage("prql", formatting);
hljs.registerLanguage("prql_no_test", formatting);
hljs.registerLanguage("prql_error", formatting);
hljs.registerLanguage("prql_no_fmt", formatting);
hljs.registerLanguage("prql,error", formatting);
hljs.registerLanguage("prql,no-fmt", formatting);
hljs.registerLanguage("elm", formatting);

// These lines should only exists in the book, not the website.
Expand All @@ -217,7 +217,7 @@ Array.from(document.querySelectorAll("code.language-prql")).forEach(
(a) => console.log(a) || hljs.highlightBlock(a)
);

Array.from(document.querySelectorAll("code.language-prql_error")).forEach(
Array.from(document.querySelectorAll("code.language-prql,error")).forEach(
(a) => console.log(a) || hljs.highlightBlock(a)
);

Expand Down
2 changes: 1 addition & 1 deletion web/book/src/examples/cte.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
```prql_no_fmt
```prql no-fmt
let newest_employees = (
from employees
sort tenure
Expand Down
2 changes: 1 addition & 1 deletion web/book/src/examples/employees.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ select [mng_name, managers.gender, salary_avg, salary_sd]

> Find distributions of titles, salaries and genders for each department.

```prql_no_fmt
```prql no-fmt
from de=dept_emp
join s=salaries side:left [
(s.emp_no == de.emp_no),
Expand Down
4 changes: 2 additions & 2 deletions web/book/src/language-features/case.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

PRQL uses `case` for both SQL's `CASE` and `IF` statements. Here's an example:

```prql_no_fmt
```prql no-fmt
from employees
derive distance = case [
city == "Calgary" => 0,
Expand All @@ -17,7 +17,7 @@ derive distance = case [
If no condition is met, the value takes a `null` value. To set a default, use a
`true` condition:

```prql_no_fmt
```prql no-fmt
from employees
derive distance = case [
city == "Calgary" => 0,
Expand Down
2 changes: 1 addition & 1 deletion web/book/src/language-features/s-strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ should implement it in PRQL or PRQL's stdlib.

To output braces from an s-string, use double braces:

```prql_no_fmt
```prql no-fmt
from employees
derive [
has_valid_title = s"regexp_contains(title, '([a-z0-9]*-){{2,}}')"
Expand Down
2 changes: 1 addition & 1 deletion web/book/src/language-features/strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ select x = """""I said """hello world"""!"""""
Strings can also contain any escape defined by
[JSON standard](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/).

```prql_no_fmt
```prql no-fmt
from my_table
select x = "\t\tline ends here\n \\ "
```
Expand Down
4 changes: 2 additions & 2 deletions web/book/src/language-features/target.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ sort age
take 10
```

```prql_no_fmt
```prql no-fmt
prql target:sql.mssql

from employees
Expand Down Expand Up @@ -47,7 +47,7 @@ very welcome.

PRQL allows specifying a version of the language in the PRQL header, like:

```prql_no_fmt
```prql no-fmt
prql version:"0.6.1"

from employees
Expand Down
55 changes: 31 additions & 24 deletions web/book/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ impl Preprocessor for ComparisonPreprocessor {
}
}

pub fn code_block_lang<'a>(event: &'a Event) -> Option<&'a str> {
/// Returns the language of a code block, divided by commas
/// For example: ```prql no-test
pub fn code_block_lang_tags(event: &Event) -> Option<Vec<String>> {
if let Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang))) = event {
Some(lang.as_ref())
Some(lang.to_string().split(' ').map(|x| x.to_string()).collect())
} else {
None
}
Expand All @@ -110,15 +112,22 @@ fn replace_examples(text: &str) -> Result<String> {

while let Some(event) = parser.next() {
// If it's not PRQL, just push it and continue
let Some(lang) = code_block_lang(&event) else {
let Some(lang_tags) = code_block_lang_tags(&event) else {
cmark_acc.push(event.to_owned());
continue;
};
if !lang.starts_with("prql") {
if !lang_tags.contains(&"prql".to_string()) {
cmark_acc.push(event.to_owned());
continue;
}

let allowed_tags = ["prql", "no-test", "no-fmt", "error"];
for tag in lang_tags.iter() {
if !allowed_tags.contains(&tag.as_str()) {
bail!("Unknown code block language: {}", tag)
}
}

let Some(Event::Text(text)) = parser.next() else {
bail!("Expected text within code block")
};
Expand All @@ -127,17 +136,10 @@ fn replace_examples(text: &str) -> Result<String> {
let options = prql_compiler::Options::default().no_signature();
let result = compile(&prql, &options);

match lang {
"prql" | "prql_no_fmt" => cmark_acc.push(Event::Html(
table_of_comparison(
&prql,
result
.map_err(|_| anyhow::anyhow!("Query raised an error:\n\n {prql}\n\n"))?
.as_str(),
)
.into(),
)),
"prql_error" => cmark_acc.push(Event::Html(
if lang_tags.contains(&"no-test".to_string()) {
cmark_acc.push(Event::Html(table_of_prql_only(&prql).into()));
} else if lang_tags.contains(&"error".to_string()) {
cmark_acc.push(Event::Html(
table_of_error(
&prql,
result
Expand All @@ -151,14 +153,19 @@ fn replace_examples(text: &str) -> Result<String> {
.as_str(),
)
.into(),
)),
"prql_no_test" => {
cmark_acc.push(Event::Html(table_of_prql_only(&prql).into()));
}
_ => {
bail!("Unknown code block language: {}", lang)
}
};
))
} else {
// Either a bare `prql` or with `no-fmt`
cmark_acc.push(Event::Html(
table_of_comparison(
&prql,
result
.map_err(|_| anyhow::anyhow!("Query raised an error:\n\n {prql}\n\n"))?
.as_str(),
)
.into(),
))
}
// Skip ending tag
parser.next();
}
Expand Down Expand Up @@ -270,7 +277,7 @@ from x
import sys
```

```prql_error
```prql error
this is an error
```
"###;
Expand Down
2 changes: 1 addition & 1 deletion web/book/src/queries/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Functions have two types of parameters:

So this function is named `fahrenheit_to_celsius` and has one parameter `temp`:

```prql_no_fmt
```prql no-fmt
func fahrenheit_to_celsius temp -> (temp - 32) / 1.8

from cities
Expand Down
4 changes: 2 additions & 2 deletions web/book/src/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function call, like `foo + bar`.

Here's a full rundown of times this applier:

```prql_no_fmt
```prql no-fmt
from employees
# Requires parentheses, because it's contains a pipe
derive is_proximate = (distance | in 0..20)
Expand Down Expand Up @@ -169,7 +169,7 @@ sort [-distance]
This doesn't work, for example (though it should provide a much better error
message):

```prql_error
```prql error
from employees
derive total_distance = sum distance
```
Expand Down
6 changes: 3 additions & 3 deletions web/book/src/transforms/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,19 @@ We can use `!` to exclude a list of columns. This can operate in two ways:

Some examples:

```prql_no_fmt
```prql no-fmt
prql target:sql.bigquery
from tracks
select ![milliseconds,bytes]
```

```prql_no_fmt
```prql no-fmt
from tracks
select [track_id, title, composer, bytes]
select ![title, composer]
```

```prql_no_fmt
```prql no-fmt
from artists
derive nick = name
select ![artists.*]
Expand Down
46 changes: 26 additions & 20 deletions web/book/tests/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use anyhow::{bail, Result};
use globset::Glob;
use insta::assert_snapshot;
use mdbook_prql::code_block_lang_tags;
use prql_compiler::*;
use std::path::{Path, PathBuf};
use std::{collections::HashMap, fs};
Expand Down Expand Up @@ -32,6 +33,10 @@ const ROOT_EXAMPLES_PATH: &str = "tests/prql";

/// Collect all the PRQL examples in the book, as a map of <Path, PRQL>.
fn collect_book_examples() -> Result<HashMap<PathBuf, String>> {
// TODO: instead of returning Strings with embedded tags (e.g. `# error`),
// we could instead return a struct with a `prql` field and a struct of its
// metadata. That would make `test_display` work by matching on the metadata
// rather than re-parsing the string.
use pulldown_cmark::{Event, Parser};
let glob = Glob::new("**/*.md")?.compile_matcher();
let examples_in_book: HashMap<PathBuf, String> = WalkDir::new(Path::new("./src/"))
Expand All @@ -46,24 +51,25 @@ fn collect_book_examples() -> Result<HashMap<PathBuf, String>> {
let mut parser = Parser::new(&text);
let mut prql_blocks = vec![];
while let Some(event) = parser.next() {
match mdbook_prql::code_block_lang(&event) {
Some(lang) if lang.starts_with("prql") => {
let mut text = String::new();
while let Some(Event::Text(line)) = parser.next() {
text.push_str(line.to_string().as_str());
}
if text.is_empty() {
bail!("Expected text after PRQL code block");
}
if lang == "prql" {
prql_blocks.push(text.to_string());
} else if lang == "prql_error" {
prql_blocks.push(format!("# Error expected\n\n{text}"));
} else if lang == "prql_no_fmt" {
prql_blocks.push(format!("# Can't yet format & compile\n\n{text}"));
}
let Some(lang_tags) = code_block_lang_tags(&event) else {
continue
};

if lang_tags.contains(&"prql".to_string()) {
let mut prql_text = String::new();
while let Some(Event::Text(line)) = parser.next() {
prql_text.push_str(line.to_string().as_str());
}
if prql_text.is_empty() {
bail!("Expected text after PRQL code block");
}
if lang_tags.contains(&"error".to_string()) {
prql_blocks.push(format!("# error\n\n{prql_text}"));
} else if lang_tags.contains(&"no-fmt".to_string()) {
prql_blocks.push(format!("# no-fmt\n\n{prql_text}"));
} else {
prql_blocks.push(prql_text.to_string());
}
_ => {}
}
}
let snapshot_prefix = &dir_entry
Expand Down Expand Up @@ -104,7 +110,7 @@ fn test_display() -> Result<(), ErrorMessages> {
collect_book_examples()?
.iter()
.try_for_each(|(path, prql)| {
if prql.contains("# Error expected") || prql.contains("# Can't yet format & compile") {
if prql.contains("# error") || prql.contains("# no-fmt") {
return Ok(());
}
prql_to_pl(prql)
Expand All @@ -114,7 +120,7 @@ fn test_display() -> Result<(), ErrorMessages> {
panic!(
"
Failed compiling the formatted result of {path:?}
To skip this test for an example, use `prql_no_fmt` as the language label.
To skip this test for an example, use `prql,no-fmt` as the language label.

The original PRQL was:

Expand All @@ -135,7 +141,7 @@ The original PRQL was:
#[test]
fn test_rq_serialize() -> Result<(), ErrorMessages> {
for (_, prql) in collect_book_examples()? {
if prql.contains("# Error expected") {
if prql.contains("# error") {
continue;
}
let rq = prql_to_pl(&prql).map(pl_to_rq)?;
Expand Down