Improve syntax highlighting in the language reference

Highlight all Slint code and don't do the preview on snippets where it
doesn't make sense.

Some snippets, such as the bare statement or expression snippets, are
now highlighted, but they are continued to be excluded from the doctest.
This commit is contained in:
Simon Hausmann 2022-03-23 11:27:08 +01:00 committed by Simon Hausmann
parent 1891e4489a
commit ed5b76e7fc
5 changed files with 46 additions and 28 deletions

View file

@ -59,7 +59,7 @@ Slint comes with a markup language that is specifically designed for user interf
powerful way to describe graphical elements, their placement, and the flow of data through the different states. It is a familiar syntax to describe the hierarchy powerful way to describe graphical elements, their placement, and the flow of data through the different states. It is a familiar syntax to describe the hierarchy
of elements and property bindings. Here's the obligatory "Hello World": of elements and property bindings. Here's the obligatory "Hello World":
.. code-block:: slint-no-preview .. code-block:: slint,ignore
HelloWorld := Window { HelloWorld := Window {
width: 400px; width: 400px;

View file

@ -148,7 +148,7 @@ You can also declare your own properties. The properties declared at the top lev
component are public and can be accessed by the component using it as an element, or using the component are public and can be accessed by the component using it as an element, or using the
language bindings: language bindings:
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
// declare a property of type int with the name `my-property` // declare a property of type int with the name `my-property`
property<int> my-property; property<int> my-property;
@ -186,7 +186,7 @@ together.
The right hand side of the `<=>` must be a reference to a property of the same type. The right hand side of the `<=>` must be a reference to a property of the same type.
The type can be omitted in a property declaration to have the type automatically inferred. The type can be omitted in a property declaration to have the type automatically inferred.
```slint ```slint,no_run
Example := Window { Example := Window {
property<brush> rect-color <=> r.background; property<brush> rect-color <=> r.background;
// it is allowed to omit the type to have it automatically inferred // it is allowed to omit the type to have it automatically inferred
@ -227,7 +227,7 @@ Anonymous structs type can be declared with curly braces: `{ identifier1: type2,
The trailing semicolon is optional. The trailing semicolon is optional.
They can be initialized with a struct literal: `{ identifier1: expression1, identifier2: expression2 }` They can be initialized with a struct literal: `{ identifier1: expression1, identifier2: expression2 }`
```slint ```slint,no_run
Example := Window { Example := Window {
property<{name: string, score: int}> player: { name: "Foo", score: 100 }; property<{name: string, score: int}> player: { name: "Foo", score: 100 };
property<{a: int, }> foo: { a: 3 }; property<{a: int, }> foo: { a: 3 };
@ -238,7 +238,7 @@ Example := Window {
It is possible to define a named struct using the `struct` keyword, It is possible to define a named struct using the `struct` keyword,
```slint ```slint,no_run
export struct Player := { export struct Player := {
name: string, name: string,
score: int, score: int,
@ -254,7 +254,7 @@ Example := Window {
The type array is using square brackets for example `[int]` is an array of `int`. In the runtime, they are The type array is using square brackets for example `[int]` is an array of `int`. In the runtime, they are
basically used as models for the `for` expression. basically used as models for the `for` expression.
```slint ```slint,no_run
Example := Window { Example := Window {
property<[int]> list-of-int: [1,2,3]; property<[int]> list-of-int: [1,2,3];
property<[{a: int, b: string}]> list-of-structs: [{ a: 1, b: "hello" }, {a: 2, b: "world"}]; property<[{a: int, b: string}]> list-of-structs: [{ a: 1, b: "hello" }, {a: 2, b: "world"}];
@ -280,7 +280,7 @@ Example := Window {
* String can be converted to float by using the `to-float` function. That function returns 0 if the string is not * String can be converted to float by using the `to-float` function. That function returns 0 if the string is not
a valid number. you can check with `is-float` if the string contains a valid number a valid number. you can check with `is-float` if the string contains a valid number
```slint ```slint,no_run
Example := Window { Example := Window {
// ok: int converts to string // ok: int converts to string
property<{a: string, b: int}> prop1: {a: 12, b: 12 }; property<{a: string, b: int}> prop1: {a: 12, b: 12 };
@ -341,7 +341,7 @@ element comes with a `clicked` callback, that's emitted when the user touches th
it with the mouse. In the example below, the emission of that callback is forwarded to another custom callback (`hello`) by declaring a it with the mouse. In the example below, the emission of that callback is forwarded to another custom callback (`hello`) by declaring a
handler and emitting our custom callback: handler and emitting our custom callback:
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
// declare a callback // declare a callback
callback hello; callback hello;
@ -358,7 +358,7 @@ Example := Rectangle {
It is also possible to add parameters to the callback. It is also possible to add parameters to the callback.
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
// declares a callback // declares a callback
callback hello(int, string); callback hello(int, string);
@ -368,7 +368,7 @@ Example := Rectangle {
And return value. And return value.
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
// declares a callback with a return value // declares a callback with a return value
callback hello(int, int) -> int; callback hello(int, int) -> int;
@ -380,7 +380,7 @@ Example := Rectangle {
It is possible to declare callback aliases in a similar way to two-way bindings: It is possible to declare callback aliases in a similar way to two-way bindings:
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
callback clicked <=> area.clicked; callback clicked <=> area.clicked;
area := TouchArea {} area := TouchArea {}
@ -394,7 +394,7 @@ are typically used to combine basic arithmetic with access to properties of othe
these properties change, the expression is automatically re-evaluated and a new value is assigned these properties change, the expression is automatically re-evaluated and a new value is assigned
to the property the expression is associated with: to the property the expression is associated with:
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
// declare a property of type int // declare a property of type int
property<int> my-property; property<int> my-property;
@ -409,7 +409,7 @@ If something changes `my-property`, the width will be updated automatically.
Arithmetic in expression with numbers works like in most programming language with the operators `*`, `+`, `-`, `/`: Arithmetic in expression with numbers works like in most programming language with the operators `*`, `+`, `-`, `/`:
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
property <int> p: 1 * 2 + 3 * 4; // same as (1 * 2) + (3 * 4) property <int> p: 1 * 2 + 3 * 4; // same as (1 * 2) + (3 * 4)
} }
@ -422,7 +422,7 @@ There are also the operators `&&` and `||` for logical *and* and *or* between bo
You can access properties by addressing the associated element, followed by a `.` and the property name: You can access properties by addressing the associated element, followed by a `.` and the property name:
```slint ```slint,no_run
Example := Rectangle { Example := Rectangle {
foo := Rectangle { foo := Rectangle {
x: 42px; x: 42px;
@ -559,25 +559,25 @@ Inside callback handlers, more complicated statements are allowed:
Assignment: Assignment:
```ignore ```slint,ignore
clicked => { some-property = 42; } clicked => { some-property = 42; }
``` ```
Self-assignment with `+=` `-=` `*=` `/=` Self-assignment with `+=` `-=` `*=` `/=`
```ignore ```slint,ignore
clicked => { some-property += 42; } clicked => { some-property += 42; }
``` ```
Calling a callback Calling a callback
```ignore ```slint,ignore
clicked => { root.some-callback(); } clicked => { root.some-callback(); }
``` ```
Conditional statements Conditional statements
```ignore ```slint,ignore
clicked => { clicked => {
if (condition) { if (condition) {
foo = 42; foo = 42;
@ -591,7 +591,7 @@ clicked => {
Empty expression Empty expression
```ignore ```slint,ignore
clicked => { } clicked => { }
// or // or
clicked => { ; } clicked => { ; }
@ -684,11 +684,13 @@ Animation can be configured with the following parameter:
It is also possible to animate several properties with the same animation: It is also possible to animate several properties with the same animation:
```ignore ```slint,ignore
animate x, y { duration: 100ms; } animate x, y { duration: 100ms; }
``` ```
is the same as is the same as
```ignore
```slint,ignore
animate x { duration: 100ms; } animate x { duration: 100ms; }
animate y { duration: 100ms; } animate y { duration: 100ms; }
``` ```
@ -1017,7 +1019,7 @@ instructions the Slint compiler to include the font and makes the font families
For example: For example:
```slint,notest ```slint,ignore
import "./NotoSans-Regular.ttf"; import "./NotoSans-Regular.ttf";
Example := Window { Example := Window {

View file

@ -6,7 +6,7 @@
<link rel="stylesheet" href="https://slint-ui.com/resources/highlightjs/11.0.1/default.min.css"> <link rel="stylesheet" href="https://slint-ui.com/resources/highlightjs/11.0.1/default.min.css">
<script src="https://slint-ui.com/resources/highlightjs/11.0.1/highlight.min.js"></script> <script src="https://slint-ui.com/resources/highlightjs/11.0.1/highlight.min.js"></script>
<script> <script>
hljs.registerLanguage("slint", function (hljs) { function highlight_slint(hljs) {
const KEYWORDS = { const KEYWORDS = {
keyword: keyword:
'struct export import signal property animate for in if states transitions parent root self', 'struct export import signal property animate for in if states transitions parent root self',
@ -49,13 +49,19 @@
], ],
illegal: /@/ illegal: /@/
}; };
}); };
// Tags used in fenced code blocks
for (let tag of ["slint", "slint,no_run", "slint,ignore"]) {
hljs.registerLanguage(tag, highlight_slint);
}
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
const rustDoc = document.querySelector('meta[name="generator"]')?.content == "rustdoc"; const rustDoc = document.querySelector('meta[name="generator"]')?.content == "rustdoc";
if (rustDoc) { if (rustDoc) {
// Only highlight .slint blocks, leave the others to rustdoc // Only highlight .slint blocks, leave the others to rustdoc
for (slintBlock of document.querySelectorAll(".language-slint")) { for (slintBlock of document.querySelectorAll("[class*=language-slint]")) {
hljs.highlightElement(slintBlock) hljs.highlightElement(slintBlock)
} }
@ -89,7 +95,7 @@
// The Sphinx/my_st generated HTML for code blocks does not use <code> tags, so highlight.js' // The Sphinx/my_st generated HTML for code blocks does not use <code> tags, so highlight.js'
// default selector "pre code" does not match. Let's do it by hand: // default selector "pre code" does not match. Let's do it by hand:
for (block of document.querySelectorAll("div.highlight-slint div.highlight pre, div.highlight-slint-no-preview div.highlight pre")) { for (block of document.querySelectorAll("div[class*=highlight-slint] div.highlight pre")) {
hljs.highlightElement(block) hljs.highlightElement(block)
} }
} }

View file

@ -31,7 +31,9 @@
async function run() { async function run() {
await slint.default(); await slint.default();
var elements = document.querySelectorAll("code.language-slint, .rustdoc pre.language-slint, div.highlight-slint"); let selector = ["code.language-slint", ".rustdoc pre.language-slint", "div.highlight-slint"]
.map((sel) => `${sel}:not([class*=slint\\,ignore]):not([class*=slint\\,no_run])`).join(",");
var elements = document.querySelectorAll(selector);
for (var i = 0; i < elements.length; ++i) { for (var i = 0; i < elements.length; ++i) {
let source = elements[i].innerText; let source = elements[i].innerText;
let div = document.createElement("div"); let div = document.createElement("div");

View file

@ -27,9 +27,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = file.replace('\r', ""); // Remove \r, because Windows. let file = file.replace('\r', ""); // Remove \r, because Windows.
let mut rest = file.as_str(); let mut rest = file.as_str();
let mut count = 0; let mut count = 0;
const BEGIN_MARKER: &str = "\n```slint\n"; const BEGIN_MARKER: &str = "\n```slint";
while let Some(begin) = rest.find(BEGIN_MARKER) { while let Some(begin) = rest.find(BEGIN_MARKER) {
rest = rest[begin..].strip_prefix(BEGIN_MARKER).unwrap(); rest = rest[begin..].strip_prefix(BEGIN_MARKER).unwrap();
// Permit `slint,no_run` but skip `slint,ignore` and others.
rest = match rest.split_once('\n') {
Some((",no_run", rest)) => rest,
Some(("", _)) => rest,
_ => continue,
};
let end = rest.find("\n```\n").ok_or_else(|| { let end = rest.find("\n```\n").ok_or_else(|| {
format!("Could not find the end of a code snippet in {}", path.display()) format!("Could not find the end of a code snippet in {}", path.display())
})?; })?;