diff --git a/archetypes/default.md b/archetypes/default.md deleted file mode 100644 index 00e77bd..0000000 --- a/archetypes/default.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "{{ replace .Name "-" " " | title }}" -date: {{ .Date }} -draft: true ---- - diff --git a/assets/css/main.css b/assets/css/main.css deleted file mode 100644 index 0cff357..0000000 --- a/assets/css/main.css +++ /dev/null @@ -1,28 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; -@import "prism.css"; - - -.form-input { - @apply focus:ring-blue-300 focus:border-blue-300 flex-grow block w-full min-w-0 rounded-none rounded-md sm:text-sm border-gray-300; -} - -.btn { - @apply border border-transparent text-sm leading-4 font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500; -} - -/* Override the margin for paragraphs in the Zxcvbn info otherwise they are too large due to the prose plugin spacing */ -#zxcvbn p { - @apply my-1; -} - -/* Hide the HIBP button in the elm-zxcvbn article (it's covered in part 2) */ -#elm-zxcvbn-app button { - display: none; -} - -/* Fix up prism.js a bit so it looks better with Tailwind Typography defaults */ -.prose pre, .prose pre[class*="language-"] { - @apply dark:bg-neutral-800 rounded-md border-0 shadow-none; -} diff --git a/assets/css/prism.css b/assets/css/prism.css deleted file mode 100644 index e2a1573..0000000 --- a/assets/css/prism.css +++ /dev/null @@ -1,3 +0,0 @@ -/* PrismJS 1.29.0 -https://prismjs.com/download.html#themes=prism-twilight&languages=clike+javascript+bash+elm+haskell+rust+shell-session */ -code[class*=language-],pre[class*=language-]{color:#fff;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;text-shadow:0 -.1em .2em #000;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}:not(pre)>code[class*=language-],pre[class*=language-]{background:#141414}pre[class*=language-]{border-radius:.5em;border:.3em solid #545454;box-shadow:1px 1px .5em #000 inset;margin:.5em 0;overflow:auto;padding:1em}pre[class*=language-]::-moz-selection{background:#27292a}pre[class*=language-]::selection{background:#27292a}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:hsla(0,0%,93%,.15)}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:hsla(0,0%,93%,.15)}:not(pre)>code[class*=language-]{border-radius:.3em;border:.13em solid #545454;box-shadow:1px 1px .3em -.1em #000 inset;padding:.15em .2em .05em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#777}.token.punctuation{opacity:.7}.token.namespace{opacity:.7}.token.boolean,.token.deleted,.token.number,.token.tag{color:#ce6849}.token.builtin,.token.constant,.token.keyword,.token.property,.token.selector,.token.symbol{color:#f9ed99}.language-css .token.string,.style .token.string,.token.attr-name,.token.attr-value,.token.char,.token.entity,.token.inserted,.token.operator,.token.string,.token.url,.token.variable{color:#909e6a}.token.atrule{color:#7385a5}.token.important,.token.regex{color:#e8c062}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.language-markup .token.attr-name,.language-markup .token.punctuation,.language-markup .token.tag{color:#ac885c}.token{position:relative;z-index:1}.line-highlight.line-highlight{background:hsla(0,0%,33%,.25);background:linear-gradient(to right,hsla(0,0%,33%,.1) 70%,hsla(0,0%,33%,0));border-bottom:1px dashed #545454;border-top:1px dashed #545454;margin-top:.75em;z-index:0}.line-highlight.line-highlight:before,.line-highlight.line-highlight[data-end]:after{background-color:#8693a6;color:#f4f1ef} diff --git a/config.toml b/config.toml index 365ff56..6048a2d 100644 --- a/config.toml +++ b/config.toml @@ -4,7 +4,8 @@ title = "broch.tech" sectionPagesMenu = "main" [params] - description = "Programming (often in Haskell and Elm), identity management, OpenID, security, Linux, other things." + description = "Programming (Rust, Haskell and Elm), identity management, OpenID, security, Linux, other things." + blogTitle = "🦥 An often infrequently updated blog" [taxonomies] tag = "tags" @@ -32,6 +33,9 @@ sectionPagesMenu = "main" [[module.mounts]] source = "hugo_stats.json" target = "assets/watching/hugo_stats.json" + [[module.imports]] + path = 'github.com/broch/broch-theme' + [build] writeStats = true diff --git a/content/posts/rust-async-fn-trait.md b/content/posts/rust-async-fn-trait.md new file mode 100644 index 0000000..f605d57 --- /dev/null +++ b/content/posts/rust-async-fn-trait.md @@ -0,0 +1,246 @@ +--- +title: Trying out Rust's Async Functions in Traits +author: Luke +date: 2023-08-31 +tags: [rust, async] +thumb: /images/rust-logo-280x160.webp +thumb_alt: The Rust 'R in a sprocket' logo +description: Exploring the latest Rust nightly features for using async functions in traits +--- + +Async functions in traits have been available in Rust nightly releases for some time now behind the feature gate `async_fn_in_trait`. The current status is summarized in the [Inside Rust blog](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html). Many of the issues are also explained in more detail by Niko Matsakis in his [Baby Steps blog](https://smallcultfollowing.com/babysteps/). + +I decided to try the feature out with some existing code which was using the [`async-trait`](https://crates.io/crates/async-trait) library (which provides a workaround for stable Rust). + +Working code for the example in this article can be found [on Github](https://github.com/tekul/rust-async-fn-trait). + +## Existing code using `async-trait` and actix + +The code I have is quite complicated but the idea can be reduced to a `Database` trait which is used by an actix web handler. There's also an axum version of the application but we'll get to that later. A toy example which models this could be written as follows: + +```rust +use async_trait::async_trait; + +pub struct Data { + id: String, +} + +#[async_trait] +pub trait Database { + async fn load_data(&self, id: &str) -> Data; +} + +struct SillyDatabase {}; + +#[async_trait] +impl Database for SillyDatabase { + async fn load_data(&self, &str id) -> Data { + Data { id: id.to_string() } + } +} + +``` + +The actix application has a function to setup the routes which is generic in the `Database` type, allowing it to be run with different backend implementations: + +```rust +use actix_web::{ + web::{self, ServiceConfig}, + App, HttpRequest, HttpResponse, HttpServer, +}; + +pub fn mk_app(backend: B) -> impl FnOnce(&mut ServiceConfig) +where + B: Database + 'static, +{ + move |app| { + app.app_data(backend).service(web::resource("/data").to(get_data::)); + } +} + +async fn get_data(req: HttpRequest) -> HttpResponse +where + B: Database + 'static +{ + let backend = req + .app_data::() + .expect("app_data should include Database"); + let Data { id } = backend.load_data("some_id").await; + HttpResponse::Ok().body(format!("Loaded data, with id {id}")) +} + +#[tokio::main] +async fn main() -> io::Result<()> { + let database = SillyDatabase {}; + let server = HttpServer::new(move || { + App::new().configure(mk_app(database.clone())) + }) + .bind("127.0.0.1:8088") + .unwrap(); + server.run().await +} + +``` + +Running the app with `cargo run` and then sending a GET request to the URL works as expected: + +```shell +$ curl localhost:8088/data +Loaded data, with id 'some_id' +``` + +### Without `async-trait` + +Ideally we can just switch to nightly rust, enable the feature, remove the `async_trait` macros and it will just work: + +```rust +#![feature(async_fn_in_trait)] + +trait Database { + async fn load_data(&self, &str id) -> Data; +} + +impl Database for SillyDatabase { + // Same as before... +} +``` + +And indeed it does! We can just run the app as before and we get the same result. Well that was easy. Looks like we can just go ahead and forget about the `async-trait` crate already? + +## Adding an axum version + +Not so fast. Let's try doing the same thing with axum. The equivalent code for the server is + +```rust +use std::net::SocketAddr; + +use async_trait::async_trait; +use axum::{extract::State, response::IntoResponse, routing::get, Router}; + +pub fn mk_app(backend: B) -> Router +where + B: Clone + Send + Sync + Database + 'static, +{ + Router::new() + .route("/data", get(get_data::)) + .with_state(backend) +} + +async fn get_data(State(backend): State) -> impl IntoResponse +where + B: Database, +{ + let Data { id } = backend.load_data("some_id").await; + format!("Loaded data, with id {id}") +} + +#[tokio::main] +async fn main() { + let backend = SillyDatabase {}; + let addr = SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 0], 8088)); + axum_server::bind(addr) + .serve(mk_app(backend).into_make_service()) + .await + .expect("server error"); +} + +``` + +This also works with the `async-trait` version of our `Database` trait. But what happens if we switch to using `async_fn_in_trait` again: + +``` +error[E0277]: the trait bound `fn(State) -> impl Future {get_data::}: Handler<_, _, _>` is not satisfied + --> src/bin/axum.rs:28:29 + | +28 | .route("/data", get(get_data::)) + | --- ^^^^^^^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(State) -> impl Future {get_data::}` + | | + | required by a bound introduced by this call + | + = note: Consider using `#[axum::debug_handler]` to improve the error message + = help: the following other types implement trait `Handler`: + as Handler> + as Handler<(), S, B>> +note: required by a bound in `axum::routing::get` +``` +Oops, it doesn't work! This opaque error message is common with axum and means that our function needs to implement `Handler` but doesn't. Axum defines Handler implementations for lots of things and we don't usually have to worry about it, but for some reason our function no longer satisfies the requirements even though it did before and we haven't changed the function directly. If we try to follow the advice to use the `axum::debug_handler` macro we will get an additional error message: + +``` +error: `#[axum_macros::debug_handler]` doesn't support generic functions + --> src/bin/axum.rs:33:18 + | +33 | async fn get_data(State(backend): State) -> impl IntoResponse + | ^^^ +``` + +So that is no help either. If we remove the generic handler and just use `SillyDatabase` directly, the problem goes away. But that's not what we want. The real code is written to be generic because the router it creates is part of a library and using a trait means users can configure it with whatever backend they want. So why isn't it working any more? + +## The `Send` bound problem + +Fortunately I'd been reading the blogs I mentioned at the start of this post and the related issues in github and there's a lot of discussion about how to make the futures returned by the `async` functions in traits implement `Send` [^matsakis-01-02-23]. This is a common requirement when using tokio's multi-threaded runtime which can move tasks about between threads, and this is why it is also a requirement for Axum. In fact, if we look at the section called [Debugging handler type errors](https://docs.rs/axum/0.6.20/axum/handler/index.html#debugging-handler-type-errors) the last point is that a handler function must: + +> Return a future that is Send. The most common way to accidentally make a future !Send is to hold a !Send type across an await. + +When we used `async-trait`, it automatically adds the `Send` bound to the function in both the trait and the implementation by default. So the `Database` trait's `load_data` function is guaranteed to return a future that is `Send`. We can check this by changing the `async_trait macro` usage to `#[async_trait(?Send)]` which no longer adds the `Send` bound. This gives us the same `Handler` error that we see above. + +When we switch to using `async_fn_in_trait` it is no longer the default to assume that the returned future must be `Send`. In our code the `get_data` handler is awaiting a future returned by the `load_data` method and thus we get the error since the future is not guaranteed to be `Send`. + +[^matsakis-01-02-23]: A good example is the article [Async trait send bounds, part 1](https://smallcultfollowing.com/babysteps/blog/2023/02/01/async-trait-send-bounds-part-1-intro/) by Niko Matsakis. + +### Using the `return_type_notation` feature + +So is there a way for our `get_data` handler to say that it only supports `Database` implementations which return a `Send` future? It turns out there is if we use the bleeding edge `return_type_notation` feature [^matsakis-13-02-23]. If we add the feature then change our `mk_app` function to use it: + +```rust +#![feature(async_fn_in_trait, return_type_notation)] +... + +pub fn mk_app(backend: B) -> Router +where + B: Clone + Send + Sync + Database + 'static, +{ + ... +} +``` + +this should fix the problem. Unfortunately we then get another error (ignoring a warning about using incomplete features): + +``` +error[E0277]: `impl Future` cannot be sent between threads safely + --> src/bin/axum.rs:46:23 + | +46 | .serve(mk_app(backend).into_make_service()) + | ------ ^^^^^^^ `impl Future` cannot be sent between threads safely + | | + | required by a bound introduced by this call +``` + +This is still complaining that our future is `!Send` even though the compiler knows that `backend` is a `SillyDatabase` at this point, which should be fine. Fortunately someone else had already run into the same problem [^rust-114142]. If we use "turbofish" syntax to explicitly tell `mk_app` what type we're using: + +```rust + axum_server::bind(addr) + .serve(mk_app::(backend).into_make_service()) + .await + .expect("server error"); + +``` + +Then our code finally compiles. + +[^rust-114142]: Fortunate for me at any rate. This [Github issue](https://github.com/rust-lang/rust/issues/114142) describes the same problem and someone helpfully provided a workaround. + +[^matsakis-13-02-23]: This is also covered in [Return type notation (send bounds, part 2)](https://smallcultfollowing.com/babysteps/blog/2023/02/13/return-type-notation-send-bounds-part-2/). Note that using it currently breaks things like Rust `tracing` macros. + +### What about Actix? + +Actix works without any issues though which is a bit confusing since it uses the same `#[tokio::main]` macro. In the past Actix had its own runtime implementation, which used multiple single-threaded tokio runtimes and did not need futures to be `Send`. Under the hood, they have retained that approach in their current architecture, so this explains why we don't have the kind of problems we get with Axum [^actix-4-release]. + +[^actix-4-release]: See the discussion in this [actix web 4 release](https://www.reddit.com/r/rust/comments/t1bim5/announcing_actix_web_v40/) announcement, for example (or read the code 🙂). + +## Conclusion + +What at first seems to be a simple enough idea, can turn out to be, well, not so simple. This has been obvious to the people implementing async Rust for a while but it's less obvious if you're just writing code like me. + +So far, I haven't had a use case where I don't want to require the `Send` bound at the trait level, but may want to require it for a specific implementation (which is what the `runtime_type_notation` achieves). It makes sense that there might be cases where this is desirable in a general purpose library. + +Hopefully in a future Rust version it will be just as easy to turn the `Send` bound on or off for our trait methods as it is with `async-trait`. For now though, sticking with the `async-trait` macros is still the simplest option. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..966175b --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module broch.tech + +// replace github.com/broch/broch-theme => /home/tekul/code/broch-theme + +go 1.21.0 + +require github.com/broch/broch-theme v0.0.0-20230824170306-a425e4bf5217 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9219325 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/broch/broch-theme v0.0.0-20230824170306-a425e4bf5217 h1:eVg57d1lgExvJUDgaxphZz5i7/2pDZ6203GerRXlV/E= +github.com/broch/broch-theme v0.0.0-20230824170306-a425e4bf5217/go.mod h1:sgAKWegkuqTEUKuA2KMaxg93EH+OS/RQb4/HQ+xDijs= diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html deleted file mode 100644 index 6956fc0..0000000 --- a/layouts/_default/baseof.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - {{ partial "head.html" . }} - - - {{ partial "navbar.html" . }} - {{ block "main" . }} - Main block - should be overridden in other templates. - {{ end }} - {{ block "footer" . }}{{ end }} - - diff --git a/layouts/_default/list.html b/layouts/_default/list.html deleted file mode 100644 index 4c5c352..0000000 --- a/layouts/_default/list.html +++ /dev/null @@ -1,19 +0,0 @@ -{{ define "main" }} -
-
-
-

{{.Title}}

-
- - {{.Content}} -
- -
-{{ end }} diff --git a/layouts/_default/single.html b/layouts/_default/single.html deleted file mode 100644 index 0f4a5ee..0000000 --- a/layouts/_default/single.html +++ /dev/null @@ -1,8 +0,0 @@ -{{ define "title" }} - - {{ .Title }} – {{ .Site.Title }} -{{ end }} -{{ define "main" }} -

{{ .Title }}

- {{ .Content }} -{{ end }} diff --git a/layouts/index.html b/layouts/index.html index a2738b4..7945f80 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -17,7 +17,7 @@

-
+
{{ .Params.thumb_alt }} diff --git a/layouts/partials/head.html b/layouts/partials/head.html deleted file mode 100644 index e2517a0..0000000 --- a/layouts/partials/head.html +++ /dev/null @@ -1,18 +0,0 @@ -{{ .Title }} - - - - -{{ $options := dict "inlineImports" true }} -{{ $styles := resources.Get "css/main.css" }} -{{ $styles = $styles | resources.PostCSS $options }} -{{ if hugo.IsProduction }} - {{ $styles = $styles | minify | fingerprint | resources.PostProcess }} -{{ end }} - - - - - - - diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html deleted file mode 100644 index 945dc4c..0000000 --- a/layouts/partials/navbar.html +++ /dev/null @@ -1,71 +0,0 @@ - - - diff --git a/layouts/partials/post-tags.html b/layouts/partials/post-tags.html deleted file mode 100644 index fa51359..0000000 --- a/layouts/partials/post-tags.html +++ /dev/null @@ -1,10 +0,0 @@ -{{ $taxo := "tags" }} -{{ with .Param $taxo }} -
- {{ range $index, $tag := . }} - {{ with $.Site.GetPage (printf "/%s/%s" $taxo $tag) -}} - {{ $tag }} - {{- end -}} - {{- end -}} -
-{{ end }} diff --git a/layouts/partials/posts-blurb.html b/layouts/partials/posts-blurb.html new file mode 100644 index 0000000..1f59bc0 --- /dev/null +++ b/layouts/partials/posts-blurb.html @@ -0,0 +1,6 @@ +

+I prefer technical articles with a bit of depth to them, so this blog isn't often updated unless I've done some research on a topic and feel it might be useful or of interest to someone. Hopefully it is :). +

+

+If you have any comments, suggestions or questions, please submit them through Github or contact me via my Github email. Pull requests or suggestions for article corrections are also very welcome. +

diff --git a/layouts/posts/posts.html b/layouts/posts/posts.html deleted file mode 100644 index dedab54..0000000 --- a/layouts/posts/posts.html +++ /dev/null @@ -1,42 +0,0 @@ -{{ define "main" }} -
-
-
-
-

🦥 An infrequently updated blog

-
-
-
-
-
-

- I prefer technical articles with a bit of depth to them, so this blog isn't often updated unless I've done some research on a topic and feel it might be useful or of interest to someone. Hopefully it is :). -

-

- If you have any comments, suggestions or questions, please submit them through Github or contact me via my Github email. Pull requests or suggestions for article corrections are also very welcome. -

-
- -
-
-
-
-
-
-{{ end }} diff --git a/layouts/posts/single.html b/layouts/posts/single.html deleted file mode 100644 index 225ea90..0000000 --- a/layouts/posts/single.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ define "main" }} -
-
-
-

{{ .Title }}

-

-{{ .Date.Format "January 2, 2006" }} -{{ if not (eq .Lastmod .Date) }} -(Updated, {{ .Lastmod.Format "January 2, 2006" }}) -{{ end }} -

-{{ .Content }} -
-
-
-{{ end }} diff --git a/layouts/rss.xml b/layouts/rss.xml deleted file mode 100644 index 2df56f0..0000000 --- a/layouts/rss.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - {{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }} - {{ .Permalink }} - Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }} - {{ with .Site.LanguageCode }}{{.}}{{end}} - {{ if not .Date.IsZero }}{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}{{ end }} - {{ with .OutputFormats.Get "RSS" }} - {{ printf "" .Permalink .MediaType | safeHTML }} - {{ end }} - {{ range first 10 .Site.RegularPages }} - - {{ .Title }} - {{ .Permalink }} - {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} - {{ .Permalink }} - {{- .Content | html -}} - - {{ end }} - - diff --git a/package.hugo.json b/package.hugo.json new file mode 100644 index 0000000..a390612 --- /dev/null +++ b/package.hugo.json @@ -0,0 +1,4 @@ +{ + "name": "broch.github.io", + "version": "0.1.0" +} \ No newline at end of file diff --git a/package.json b/package.json index afd832f..57fa6b4 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,17 @@ { - "name": "broch-hugo", - "version": "1.0.0", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "comments": { + "dependencies": {}, + "devDependencies": { + "@tailwindcss/forms": "github.com/broch/broch-theme", + "@tailwindcss/typography": "github.com/broch/broch-theme", + "autoprefixer": "github.com/broch/broch-theme", + "postcss": "github.com/broch/broch-theme", + "postcss-cli": "github.com/broch/broch-theme", + "postcss-import": "github.com/broch/broch-theme", + "tailwindcss": "github.com/broch/broch-theme" + } }, - "repository": { - "type": "git", - "url": "git+https://github.com/broch/broch.github.io.git" - }, - "author": "", - "bugs": { - "url": "https://github.com/broch/broch.github.io/issues" - }, - "homepage": "https://broch.tech", + "dependencies": {}, "devDependencies": { "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.9", @@ -21,5 +20,7 @@ "postcss-cli": "^10.1.0", "postcss-import": "^14.0.1", "tailwindcss": "^3.3.3" - } + }, + "name": "broch.github.io", + "version": "0.1.0" } diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 1e71298..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - plugins: { - "postcss-import": {}, - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/static/images/broch.svg b/static/images/broch.svg deleted file mode 100644 index c5c2a49..0000000 --- a/static/images/broch.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - Broch Logo - - - diff --git a/static/images/logo.svg b/static/images/logo.svg new file mode 100644 index 0000000..61a441e --- /dev/null +++ b/static/images/logo.svg @@ -0,0 +1,23 @@ + + + + Broch Logo + + diff --git a/static/images/rust-logo-280x160.webp b/static/images/rust-logo-280x160.webp new file mode 100644 index 0000000..3001e34 Binary files /dev/null and b/static/images/rust-logo-280x160.webp differ diff --git a/static/js/prism.js b/static/js/prism.js deleted file mode 100644 index 6c46ccc..0000000 --- a/static/js/prism.js +++ /dev/null @@ -1,10 +0,0 @@ -/* PrismJS 1.29.0 -https://prismjs.com/download.html#themes=prism-twilight&languages=clike+javascript+bash+elm+haskell+rust+shell-session */ -var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(jg.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a"+i.content+""},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); -Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; -Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript; -!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i:&|^?%#@~!]{2,}|[+\-/*=$<>:&|^?%#@~!]/,hvariable:/\b(?:[A-Z]\w*\.)*[a-z]\w*\b/,constant:/\b(?:[A-Z]\w*\.)*[A-Z]\w*\b/,punctuation:/[{}[\]|(),.:]/}; -Prism.languages.haskell={comment:{pattern:/(^|[^-!#$%*+=?&@|~.:<>^\\\/])(?:--(?:(?=.)[^-!#$%*+=?&@|~.:<>^\\\/].*|$)|\{-[\s\S]*?-\})/m,lookbehind:!0},char:{pattern:/'(?:[^\\']|\\(?:[abfnrtv\\"'&]|\^[A-Z@[\]^_]|ACK|BEL|BS|CAN|CR|DC1|DC2|DC3|DC4|DEL|DLE|EM|ENQ|EOT|ESC|ETB|ETX|FF|FS|GS|HT|LF|NAK|NUL|RS|SI|SO|SOH|SP|STX|SUB|SYN|US|VT|\d+|o[0-7]+|x[0-9a-fA-F]+))'/,alias:"string"},string:{pattern:/"(?:[^\\"]|\\(?:\S|\s+\\))*"/,greedy:!0},keyword:/\b(?:case|class|data|deriving|do|else|if|in|infixl|infixr|instance|let|module|newtype|of|primitive|then|type|where)\b/,"import-statement":{pattern:/(^[\t ]*)import\s+(?:qualified\s+)?(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*(?:\s+as\s+(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*)?(?:\s+hiding\b)?/m,lookbehind:!0,inside:{keyword:/\b(?:as|hiding|import|qualified)\b/,punctuation:/\./}},builtin:/\b(?:abs|acos|acosh|all|and|any|appendFile|approxRational|asTypeOf|asin|asinh|atan|atan2|atanh|basicIORun|break|catch|ceiling|chr|compare|concat|concatMap|const|cos|cosh|curry|cycle|decodeFloat|denominator|digitToInt|div|divMod|drop|dropWhile|either|elem|encodeFloat|enumFrom|enumFromThen|enumFromThenTo|enumFromTo|error|even|exp|exponent|fail|filter|flip|floatDigits|floatRadix|floatRange|floor|fmap|foldl|foldl1|foldr|foldr1|fromDouble|fromEnum|fromInt|fromInteger|fromIntegral|fromRational|fst|gcd|getChar|getContents|getLine|group|head|id|inRange|index|init|intToDigit|interact|ioError|isAlpha|isAlphaNum|isAscii|isControl|isDenormalized|isDigit|isHexDigit|isIEEE|isInfinite|isLower|isNaN|isNegativeZero|isOctDigit|isPrint|isSpace|isUpper|iterate|last|lcm|length|lex|lexDigits|lexLitChar|lines|log|logBase|lookup|map|mapM|mapM_|max|maxBound|maximum|maybe|min|minBound|minimum|mod|negate|not|notElem|null|numerator|odd|or|ord|otherwise|pack|pi|pred|primExitWith|print|product|properFraction|putChar|putStr|putStrLn|quot|quotRem|range|rangeSize|read|readDec|readFile|readFloat|readHex|readIO|readInt|readList|readLitChar|readLn|readOct|readParen|readSigned|reads|readsPrec|realToFrac|recip|rem|repeat|replicate|return|reverse|round|scaleFloat|scanl|scanl1|scanr|scanr1|seq|sequence|sequence_|show|showChar|showInt|showList|showLitChar|showParen|showSigned|showString|shows|showsPrec|significand|signum|sin|sinh|snd|sort|span|splitAt|sqrt|subtract|succ|sum|tail|take|takeWhile|tan|tanh|threadToIOResult|toEnum|toInt|toInteger|toLower|toRational|toUpper|truncate|uncurry|undefined|unlines|until|unwords|unzip|unzip3|userError|words|writeFile|zip|zip3|zipWith|zipWith3)\b/,number:/\b(?:\d+(?:\.\d+)?(?:e[+-]?\d+)?|0o[0-7]+|0x[0-9a-f]+)\b/i,operator:[{pattern:/`(?:[A-Z][\w']*\.)*[_a-z][\w']*`/,greedy:!0},{pattern:/(\s)\.(?=\s)/,lookbehind:!0},/[-!#$%*+=?&@|~:<>^\\\/][-!#$%*+=?&@|~.:<>^\\\/]*|\.[-!#$%*+=?&@|~.:<>^\\\/]+/],hvariable:{pattern:/\b(?:[A-Z][\w']*\.)*[_a-z][\w']*/,inside:{punctuation:/\./}},constant:{pattern:/\b(?:[A-Z][\w']*\.)*[A-Z][\w']*/,inside:{punctuation:/\./}},punctuation:/[{}[\];(),.:]/},Prism.languages.hs=Prism.languages.haskell; -!function(e){for(var a="/\\*(?:[^*/]|\\*(?!/)|/(?!\\*)|)*\\*/",t=0;t<2;t++)a=a.replace(//g,(function(){return a}));a=a.replace(//g,(function(){return"[^\\s\\S]"})),e.languages.rust={comment:[{pattern:RegExp("(^|[^\\\\])"+a),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|trait|type|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:Self|abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:bool|char|f(?:32|64)|[ui](?:8|16|32|64|128|size)|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(Prism); -!function(s){var n=['"(?:\\\\[^]|\\$\\([^)]+\\)|\\$(?!\\()|`[^`]+`|[^"\\\\`$])*"',"'[^']*'","\\$'(?:[^'\\\\]|\\\\[^])*'","<<-?\\s*([\"']?)(\\w+)\\1\\s[^]*?[\r\n]\\2"].join("|");s.languages["shell-session"]={command:{pattern:RegExp('^(?:[^\\s@:$#%*!/\\\\]+@[^\r\n@:$#%*!/\\\\]+(?::[^\0-\\x1F$#%*?"<>:;|]+)?|[/~.][^\0-\\x1F$#%*?"<>@:;|]*)?[$#%](?=\\s)'+"(?:[^\\\\\r\n \t'\"<$]|[ \t](?:(?!#)|#.*$)|\\\\(?:[^\r]|\r\n?)|\\$(?!')|<(?!<)|<>)+".replace(/<>/g,(function(){return n})),"m"),greedy:!0,inside:{info:{pattern:/^[^#$%]+/,alias:"punctuation",inside:{user:/^[^\s@:$#%*!/\\]+@[^\r\n@:$#%*!/\\]+/,punctuation:/:/,path:/[\s\S]+/}},bash:{pattern:/(^[$#%]\s*)\S[\s\S]*/,lookbehind:!0,alias:"language-bash",inside:s.languages.bash},"shell-symbol":{pattern:/^[$#%]/,alias:"important"}}},output:/.(?:.*(?:[\r\n]|.$))*/},s.languages["sh-session"]=s.languages.shellsession=s.languages["shell-session"]}(Prism); diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index aa1079e..0000000 --- a/tailwind.config.js +++ /dev/null @@ -1,24 +0,0 @@ -const defaultTheme = require('tailwindcss/defaultTheme') - -module.exports = { - content: [ './hugo_stats.json' ], - theme: { - extend: { - fontFamily: { - sans: ['Inter var', ...defaultTheme.fontFamily.sans], - }, - typography: { - DEFAULT: { - css: { - "code::before": { content: '' }, - "code::after": { content: '' }, - }, - }, - }, - }, - }, - plugins: [ - require('@tailwindcss/typography'), - require('@tailwindcss/forms'), - ], -}