[hljs] Añade soporte a HighlightJS en PageTop

This commit is contained in:
Manuel Cillero 2024-12-22 18:09:01 +01:00
parent 6701fb3e90
commit 7530cc69c2
210 changed files with 5277 additions and 2 deletions

View file

@ -0,0 +1,58 @@
//! Configuration settings for package.
//!
//! Example:
//!
//! ```toml
//! [hljs]
//! mode = "core"
//! theme = "zenburn"
//! tabsize = 8
//! ```
//!
//! Usage:
//!
//! ```rust
//! use pagetop_hljs::config;
//!
//! assert_eq!(config::SETTINGS.hljs.theme, "zenburn");
//! ```
//! See [`pagetop::config`] to learn how PageTop reads configuration files and uses settings.
use pagetop::prelude::*;
use crate::hljs_mode::HljsMode;
use crate::hljs_theme::HljsTheme;
use serde::Deserialize;
include_config!(SETTINGS: Settings => [
// [hljs]
"hljs.mode" => "core",
"hljs.theme" => "default",
"hljs.tabsize" => 4,
]);
#[derive(Debug, Deserialize)]
/// Configuration settings for the [`[hljs]`](Hljs) section (see [`SETTINGS`] package).
pub struct Settings {
pub hljs: Hljs,
}
#[derive(Debug, Deserialize)]
/// Section `[hljs]` of the configuration settings.
///
/// See [`Settings`].
pub struct Hljs {
/// Use ***core*** to import a minimal library and load only the languages added via
/// [`add_hljs_language()`](crate::hljs_context::HljsContext::add_hljs_language). Alternatively,
/// ***common*** imports an extended library containing around 40 popular languages (see
/// [`HljsLang`](crate::hljs_lang::HljsLang)). Note that using the *common* library restricts
/// you to the languages that are preloaded.
/// Default value: *"core"*
pub mode: HljsMode,
/// Default theme in kebab-case used to display code snippets on web pages (see [`HljsTheme`]).
/// Default value: *"default"*
pub theme: HljsTheme,
/// Number of spaces for *tab* character.
/// Default value: *4*
pub tabsize: usize,
}

View file

@ -0,0 +1,95 @@
use pagetop::prelude::*;
use crate::config;
use crate::hljs_lang::HljsLang;
use crate::hljs_mode::HljsMode;
use crate::hljs_theme::HljsTheme;
use std::collections::HashSet;
// Context parameters.
const PARAM_HLJS_ENABLED: &str = "hljs.enabled";
const PARAM_HLJS_MODE: &str = "hljs.mode";
const PARAM_HLJS_LANGS: &str = "hljs.langs";
const PARAM_HLJS_THEME: &str = "hljs.theme";
/// Extend Context with HighlightJS features.
pub trait HljsContext {
/// Enable syntax highlighting in current context.
fn enable_hljs(&mut self);
/// Preventing syntax highlighting in current context.
fn disable_hljs(&mut self);
/// Force the use of the *highlight.js* ***core*** or ***common*** mode in current context,
/// ignoring the [`config::SETTINGS.hljs.mode`](crate::config::Hljs#structfield.mode)
/// configuration setting.
fn force_hljs_mode(&mut self, mode: &HljsMode);
/// Add a new language to the context for processing code snippets. It is necessary to add at
/// least one language to load the *highlight.js* library. Each
/// [`Snippet`](crate::snippet::Snippet) component automatically adds its required language.
fn add_hljs_language(&mut self, language: &HljsLang);
/// Change the theme in current context for displaying code snippets. The same theme is used for
/// all snippets in the given context.
fn set_hljs_theme(&mut self, theme: &HljsTheme);
fn is_hljs_enabled(&self) -> bool;
fn hljs_mode(&self) -> HljsMode;
fn hljs_languages(&self) -> Option<HashSet<String>>;
fn hljs_theme(&self) -> HljsTheme;
}
impl HljsContext for Context {
fn enable_hljs(&mut self) {
self.alter_param::<bool>(PARAM_HLJS_ENABLED, &true);
}
fn disable_hljs(&mut self) {
self.alter_param::<bool>(PARAM_HLJS_ENABLED, &false);
}
fn force_hljs_mode(&mut self, mode: &HljsMode) {
self.alter_param::<HljsMode>(PARAM_HLJS_MODE, mode);
}
fn add_hljs_language(&mut self, language: &HljsLang) {
let languages = match self.get_param::<String>(PARAM_HLJS_LANGS) {
Ok(previous) => join_string!(previous, ";", language.to_string()),
_ => language.to_string(),
};
self.alter_param::<String>(PARAM_HLJS_LANGS, &languages);
}
fn set_hljs_theme(&mut self, theme: &HljsTheme) {
self.alter_param::<String>(PARAM_HLJS_THEME, &theme.to_string());
}
// HljsContext GETTERS.
fn is_hljs_enabled(&self) -> bool {
self.get_param::<bool>(PARAM_HLJS_ENABLED).unwrap_or(true)
}
fn hljs_mode(&self) -> HljsMode {
self.get_param::<HljsMode>(PARAM_HLJS_MODE)
.unwrap_or(config::SETTINGS.hljs.mode)
}
fn hljs_languages(&self) -> Option<HashSet<String>> {
if let Ok(languages) = self.get_param::<String>(PARAM_HLJS_LANGS) {
let set: HashSet<String> = languages.split(';').map(|s| s.to_string()).collect();
return Some(set);
}
None
}
fn hljs_theme(&self) -> HljsTheme {
self.get_param::<HljsTheme>(PARAM_HLJS_THEME)
.unwrap_or(config::SETTINGS.hljs.theme)
}
}

View file

@ -0,0 +1,246 @@
use pagetop::prelude::*;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::LazyLock;
use std::fmt;
/// Supported coding languages.
///
/// Languages are represented by *PascalCase* enums within the code and are mapped to corresponding
/// [highlight.js](https://highlightjs.org/) language names.
///
/// ```rust
/// use pagetop_hljs::HljsLang;
///
/// assert_eq!(HljsLang::CoffeeScript.to_string(), "coffeescript".to_string());
/// ```
#[derive(AutoDefault, Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum HljsLang {
// Common languages.
Bash,
C,
Cpp,
Csharp,
CSS,
Diff,
Go,
GraphQL,
HTML,
INI,
Java,
JavaScript,
JSON,
Kotlin,
Less,
Lua,
Makefile,
Markdown,
ObjectiveC,
Perl,
PHP,
PHPTemplate,
#[default]
Plaintext,
Python,
PythonREPL,
R,
Ruby,
Rust,
SCSS,
ShellSession,
SQL,
Swift,
TOML,
TypeScript,
VisualBasicNET,
WebAssembly,
XML,
/// Enum variants for languages ranging from `Bash` to `YAML` are all preloaded in the
/// ***common*** mode. To include additional languages, use the default ***core*** mode.
///
/// See [`config::SETTINGS.hljs.mode`](crate::config::Hljs#structfield.mode).
YAML,
// Additional languages.
ActionScript,
Ada,
Apache,
AppleScript,
Arduino,
ARMAssembly,
AsciiDoc,
AspectJ,
AutoHotkey,
AVRAssembly,
Awk,
BASIC,
Clojure,
ClojureREPL,
CMake,
CoffeeScript,
Crystal,
D,
Dart,
Delphy,
Django,
DNSZone,
Dockerfile,
DOS,
Elixir,
Elm,
ERB,
Erlang,
ErlangREPL,
Fortran,
Fsharp,
Handlebars,
Haskell,
HTTP,
Julia,
JuliaREPL,
LaTeX,
Lisp,
LLVMIR,
Matlab,
Nginx,
NodeREPL,
Ocaml,
PostgreSQL,
PowerShell,
Prolog,
Properties,
Scala,
Scheme,
Scilab,
Smalltalk,
Tcl,
Twig,
VBScript,
X86Asm,
}
static HLJS_LANGS: LazyLock<HashMap<HljsLang, &'static str>> = LazyLock::new(|| {
use HljsLang::*;
kv![
// Common languages.
Bash => "bash",
C => "c",
Cpp => "cpp",
Csharp => "csharp",
CSS => "css",
Diff => "diff",
Go => "go",
GraphQL => "graphql",
HTML => "html,xml",
INI => "ini",
Java => "java",
JavaScript => "javascript",
JSON => "json",
Kotlin => "kotlin",
Less => "less",
Lua => "lua",
Makefile => "makefile",
Markdown => "markdown",
ObjectiveC => "objectivec",
Perl => "perl",
PHP => "php",
PHPTemplate => "php-template",
Plaintext => "plaintext",
Python => "python",
PythonREPL => "python-repl",
R => "r",
Ruby => "ruby",
Rust => "rust",
SCSS => "scss",
ShellSession => "shell",
SQL => "sql",
Swift => "swift",
TOML => "toml,ini",
TypeScript => "typescript",
VisualBasicNET => "vbnet",
WebAssembly => "wasm",
XML => "xml",
YAML => "yaml",
// Additional languages.
ActionScript => "actionscript",
Ada => "ada",
Apache => "apache",
AppleScript => "applescript",
Arduino => "arduino",
ARMAssembly => "armasm",
AsciiDoc => "asciidoc",
AspectJ => "aspectj",
AutoHotkey => "autohotkey",
AVRAssembly => "avrasm",
Awk => "awk",
BASIC => "basic",
Clojure => "clojure",
ClojureREPL => "clojure-repl",
CMake => "cmake",
CoffeeScript => "coffeescript",
Crystal => "crystal",
D => "d",
Dart => "dart",
Delphy => "delphy",
Django => "django",
DNSZone => "dns",
Dockerfile => "dockerfile",
DOS => "dos",
Elixir => "elixir",
Elm => "elm",
ERB => "erb",
Erlang => "erlang",
ErlangREPL => "erlang-repl",
Fortran => "fortran",
Fsharp => "fsharp",
Handlebars => "handlebars",
Haskell => "haskell",
HTTP => "http",
Julia => "julia",
JuliaREPL => "julia-repl",
LaTeX => "latex",
Lisp => "lisp",
LLVMIR => "llvm",
Matlab => "matlab",
Nginx => "nginx",
NodeREPL => "node-repl",
Ocaml => "ocaml",
PostgreSQL => "pgsql",
PowerShell => "powershell",
Prolog => "prolog",
Properties => "properties",
Scala => "scala",
Scheme => "scheme",
Scilab => "scilab",
Smalltalk => "smalltalk",
Tcl => "tcl",
Twig => "twig",
VBScript => "vbscript",
X86Asm => "x86asm",
]
});
impl ToString for HljsLang {
fn to_string(&self) -> String {
String::from(*HLJS_LANGS.get(self).unwrap())
}
}
impl FromStr for HljsLang {
type Err = fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
HLJS_LANGS
.iter()
.find_map(|(&key, &value)| if value == s { Some(key) } else { None })
.ok_or_else(|| fmt::Error)
}
}
impl HljsLang {
pub(crate) fn to_url(language: impl Into<String>) -> String {
let language = language.into();
join_string!("/hljs/js/languages/", language, ".min.js")
}
}

View file

@ -0,0 +1,41 @@
use serde::{Deserialize, Deserializer};
use std::fmt;
use std::str::FromStr;
#[derive(Clone, Copy, Debug)]
pub enum HljsMode {
Core,
Common,
}
impl ToString for HljsMode {
fn to_string(&self) -> String {
String::from(match self {
HljsMode::Core => "core",
HljsMode::Common => "common",
})
}
}
impl FromStr for HljsMode {
type Err = fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"core" => Ok(HljsMode::Core),
"common" => Ok(HljsMode::Common),
_ => Err(fmt::Error),
}
}
}
impl<'de> Deserialize<'de> for HljsMode {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
HljsMode::from_str(&s).map_err(serde::de::Error::custom)
}
}

View file

@ -0,0 +1,257 @@
use pagetop::prelude::*;
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::LazyLock;
use std::fmt;
/// Supported themes.
///
/// Themes are defined as *PascalCase* enums in the code and correspond to *kebab-case* string
/// identifiers.
///
/// ```rust
/// use pagetop_hljs::HljsTheme;
///
/// assert_eq!(HljsTheme::AtelierPlateauLight.to_string(), "atelier-plateau-light".to_string());
/// ```
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum HljsTheme {
A11yDark,
A11yLight,
Agate,
AnOldHope,
Androidstudio,
ArduinoLight,
Arta,
Ascetic,
AtelierCave,
AtelierCaveLight,
AtelierDune,
AtelierDuneLight,
AtelierEstuary,
AtelierEstuaryLight,
AtelierForest,
AtelierForestLight,
AtelierHeath,
AtelierHeathLight,
AtelierLakeside,
AtelierLakesideLight,
AtelierPlateau,
AtelierPlateauLight,
AtelierSavanna,
AtelierSavannaLight,
AtelierSeaside,
AtelierSeasideLight,
AtelierSulphurpool,
AtelierSulphurpoolLight,
AtomOneDark,
AtomOneDarkReasonable,
AtomOneLight,
BrownPaper,
CodepenEmbed,
ColorBrewer,
Darcula,
Dark,
Default,
Devibeans,
Docco,
Dracula,
Far,
Foundation,
Framer,
Gigavolt,
Github,
Gml,
Googlecode,
GradientDark,
GradientLight,
Grayscale,
GruvboxDarkHard,
GruvboxLightHard,
Hopscotch,
Hybrid,
Idea,
IrBlack,
KimbieDark,
KimbieLight,
Lightfair,
Lioshi,
Magula,
MonoBlue,
MonokaiSublime,
NightOwl,
NnfxDark,
NnfxLight,
Obsidian,
Ocean,
Oceanicnext,
PandaSyntaxDark,
PandaSyntaxLight,
Pojoaque,
Purebasic,
QtcreatorDark,
QtcreatorLight,
Railcasts,
Rainbow,
Routeros,
SchoolBook,
ShapesOfPurple,
SolarizedDark,
SolarizedLight,
Srcery,
StackoverflowDark,
StackoverflowLight,
Sunburst,
TokioNightDark,
TokioNightLight,
Tomorrow,
TomorrowNight,
TomorrowNightBlue,
TomorrowNightBright,
Vs,
Vs2015,
Xcode,
Xt256,
Zenburn,
}
static HLJS_THEMES: LazyLock<HashMap<HljsTheme, &'static str>> = LazyLock::new(|| {
use HljsTheme::*;
kv![
A11yDark => "a11y-dark",
A11yLight => "a11y-light",
Agate => "agate",
AnOldHope => "an-old-hope",
Androidstudio => "androidstudio",
ArduinoLight => "arduino-light",
Arta => "arta",
Ascetic => "ascetic",
AtelierCave => "atelier-cave", // base16
AtelierCaveLight => "atelier-cave-light", // base16
AtelierDune => "atelier-dune", // base16
AtelierDuneLight => "atelier-dune-light", // base16
AtelierEstuary => "atelier-estuary", // base16
AtelierEstuaryLight => "atelier-estuary-light", // base16
AtelierForest => "atelier-forest", // base16
AtelierForestLight => "atelier-forest-light", // base16
AtelierHeath => "atelier-heath", // base16
AtelierHeathLight => "atelier-heath-light", // base16
AtelierLakeside => "atelier-lakeside", // base16
AtelierLakesideLight => "atelier-lakeside-light", // base16
AtelierPlateau => "atelier-plateau", // base16
AtelierPlateauLight => "atelier-plateau-light", // base16
AtelierSavanna => "atelier-savanna", // base16
AtelierSavannaLight => "atelier-savanna-light", // base16
AtelierSeaside => "atelier-seaside", // base16
AtelierSeasideLight => "atelier-seaside-light", // base16
AtelierSulphurpool => "atelier-sulphurpool", // base16
AtelierSulphurpoolLight => "atelier-sulphurpool-light", // base16
AtomOneDark => "atom-one-dark",
AtomOneDarkReasonable => "atom-one-dark-reasonable",
AtomOneLight => "atom-one-light",
BrownPaper => "brown-paper",
CodepenEmbed => "codepen-embed",
ColorBrewer => "color-brewer",
Darcula => "darcula", // base16
Dark => "dark",
Default => "default",
Devibeans => "devibeans",
Docco => "docco",
Dracula => "dracula", // base16
Far => "far",
Foundation => "foundation",
Framer => "framer", // base16
Gigavolt => "gigavolt", // base16
Github => "github",
Gml => "gml",
Googlecode => "googlecode",
GradientDark => "gradient-dark",
GradientLight => "gradient-light",
Grayscale => "grayscale",
GruvboxDarkHard => "gruvbox-dark-hard", // base16
GruvboxLightHard => "gruvbox-light-hard", // base16
Hopscotch => "hopscotch", // base16
Hybrid => "hybrid",
Idea => "idea",
IrBlack => "ir-black",
KimbieDark => "kimbie-dark",
KimbieLight => "kimbie-light",
Lightfair => "lightfair",
Lioshi => "lioshi",
Magula => "magula",
MonoBlue => "mono-blue",
MonokaiSublime => "monokai-sublime",
NightOwl => "night-owl",
NnfxDark => "nnfx-dark",
NnfxLight => "nnfx-light",
Obsidian => "obsidian",
Ocean => "ocean", // base16
Oceanicnext => "oceanicnext", // base16
PandaSyntaxDark => "panda-syntax-dark",
PandaSyntaxLight => "panda-syntax-light",
Pojoaque => "pojoaque",
Purebasic => "purebasic",
QtcreatorDark => "qtcreator-dark",
QtcreatorLight => "qtcreator-light",
Railcasts => "railcasts", // base16
Rainbow => "rainbow",
Routeros => "routeros",
SchoolBook => "school-book",
ShapesOfPurple => "shapes-of-purple",
SolarizedDark => "solarized-dark", // base16
SolarizedLight => "solarized-light", // base16
Srcery => "srcery",
StackoverflowDark => "stackoverflow-dark",
StackoverflowLight => "stackoverflow-light",
Sunburst => "sunburst",
TokioNightDark => "tokio-night-dark",
TokioNightLight => "tokio-night-light",
Tomorrow => "tomorrow", // base16
TomorrowNight => "tomorrow-night", // base16
TomorrowNightBlue => "tomorrow-night-blue",
TomorrowNightBright => "tomorrow-night-bright",
Vs => "vs",
Vs2015 => "vs2015",
Xcode => "xcode",
Xt256 => "xt256",
Zenburn => "zenburn", // base16
]
});
impl ToString for HljsTheme {
fn to_string(&self) -> String {
String::from(*HLJS_THEMES.get(self).unwrap())
}
}
impl FromStr for HljsTheme {
type Err = fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
HLJS_THEMES
.iter()
.find_map(|(&key, &value)| if value == s { Some(key) } else { None })
.ok_or_else(|| fmt::Error)
}
}
impl<'de> Deserialize<'de> for HljsTheme {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
HljsTheme::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl HljsTheme {
pub(crate) fn to_url(theme: impl Into<String>) -> String {
let theme = theme.into();
join_string!("/hljs/css/", theme, ".min.css")
}
}

View file

@ -0,0 +1,166 @@
//! <div align="center">
//!
//! <h1>PageTop HighlightJS</h1>
//!
//! <p>Integra <a href="https://highlightjs.org">highlight.js</a> para mostrar fragmentos de código con resaltado de sintaxis con <strong>PageTop</strong>.</p>
//!
//! [![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](#-license)
//! [![Doc API](https://img.shields.io/docsrs/pagetop-hljs?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-hljs)
//! [![Crates.io](https://img.shields.io/crates/v/pagetop-hljs.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-hljs)
//! [![Descargas](https://img.shields.io/crates/d/pagetop-hljs.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-hljs)
//!
//! </div>
//!
//! ## Uso
//!
//! Añade `pagetop-hljs` a tu archivo `Cargo.toml`:
//!
//! ```rust
//! [dependencies]
//! pagetop-hljs = "<Version>"
//! ```
//!
//! Incluye `pagetop_hljs::HighlightJS` en las dependencias del paquete o aplicación que lo requiera:
//!
//! ```rust
//! use pagetop::prelude::*;
//!
//! impl PackageTrait for MyPackage {
//! // ...
//! fn dependencies(&self) -> Vec<PackageRef> {
//! vec![
//! // ...
//! &pagetop_hljs::HighlightJS,
//! // ...
//! ]
//! }
//!
//! fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
//! cfg.route("/snippet", service::web::get().to(hljs_sample));
//! }
//! // ...
//! }
//! ```
//!
//! Y finalmente añade tus fragmentos de código con resaltado de sintaxis en páginas web:
//!
//! ```rust
//! use pagetop_hljs::prelude::*;
//!
//! async fn hljs_sample(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
//! Page::new(request)
//! .with_component(Snippet::with(
//! HljsLang::Rust,
//! r###"
//! // This is the main function.
//! fn main() {
//! // Print text to the console.
//! println!("Hello World!");
//! }
//! "###,
//! ))
//! .render()
//! }
//! ```
#![doc(
html_favicon_url = "https://raw.githubusercontent.com/manuelcillero/pagetop/main/static/favicon.ico"
)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/manuelcillero/pagetop/main/static/pagetop_hljs.png"
)]
use pagetop::prelude::*;
// API *********************************************************************************************
pub mod config;
pub mod hljs_context;
pub mod hljs_lang;
pub mod hljs_mode;
pub mod hljs_theme;
pub mod snippet;
// PRELUDE *****************************************************************************************
pub mod prelude {
pub use crate::hljs_context::HljsContext;
pub use crate::hljs_lang::HljsLang;
pub use crate::hljs_mode::HljsMode;
pub use crate::hljs_theme::HljsTheme;
pub use crate::snippet::Snippet;
}
include_files!(hljs);
include_locales!(LOCALES_HLJS);
/// Implementa [`PackageTrait`].
pub struct HighlightJS;
impl PackageTrait for HighlightJS {
fn description(&self) -> L10n {
L10n::t("hljs_description", &LOCALES_HLJS)
}
fn actions(&self) -> Vec<ActionBox> {
actions![action::page::AfterRenderBody::new(after_render_body)]
}
fn configure_service(&self, cfg: &mut service::web::ServiceConfig) {
include_files_service!(cfg, hljs => "/hljs");
}
}
// Versión de la librería Highlight.js.
const HLJS_VERSION: &str = "11.7.0";
// Define los recursos para la página según se use highlight.js en su versión "core" o "common".
fn after_render_body(page: &mut Page) {
use hljs_context::HljsContext;
use hljs_lang::HljsLang;
use hljs_mode::HljsMode;
use hljs_theme::HljsTheme;
let cx = page.context();
if cx.is_hljs_enabled() {
if let Some(languages) = cx.hljs_languages() {
match cx.hljs_mode() {
HljsMode::Core => {
cx.alter_assets(AssetsOp::AddJavaScript(
JavaScript::from("/hljs/js/core.min.js").with_version(HLJS_VERSION),
));
for l in languages {
cx.alter_assets(AssetsOp::AddJavaScript(
JavaScript::from(HljsLang::to_url(l)).with_version(HLJS_VERSION),
));
}
}
_ => {
cx.alter_assets(AssetsOp::AddJavaScript(
JavaScript::from("/hljs/js/highlight.min.js").with_version(HLJS_VERSION),
));
}
}
// Configura highlight.js (deshabilitando autodetección del lenguaje).
#[rustfmt::skip]
cx.alter_assets(AssetsOp::AddJavaScript(
JavaScript::inline("highlight.js", join_string!("
hljs.configure({
tabReplace: '", " ".repeat(config::SETTINGS.hljs.tabsize), "',
languages: [],
});
hljs.highlightAll();
")),
));
cx.alter_assets(AssetsOp::AddStyleSheet(
StyleSheet::from(HljsTheme::to_url(cx.hljs_theme().to_string()))
.with_version(HLJS_VERSION),
));
}
}
}

View file

@ -0,0 +1 @@
hljs_description = Display beautiful code snippets on web pages using the highlight.js library.

View file

@ -0,0 +1 @@
hljs_description = Incorpora fragmentos de código elegantes en páginas web usando la biblioteca highlight.js.

View file

@ -0,0 +1,63 @@
//! Add a new component to put code snippets on web pages.
use pagetop::prelude::*;
use crate::hljs_context::HljsContext;
use crate::hljs_lang::HljsLang;
#[derive(AutoDefault)]
/// Component to put code snippets on web pages.
pub struct Snippet {
language: HljsLang,
snippet: String,
}
impl ComponentTrait for Snippet {
fn new() -> Self {
Snippet::default()
}
fn setup_before_prepare(&mut self, cx: &mut Context) {
cx.add_hljs_language(self.language());
}
fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup {
PrepareMarkup::With(html! {
pre {
code class=(join_string!("language-", self.language().to_string())) {
(self.snippet())
}
}
})
}
}
impl Snippet {
pub fn with(language: HljsLang, code: impl Into<String>) -> Self {
Snippet::new().with_language(language).with_snippet(code)
}
// Hljs BUILDER.
#[fn_builder]
pub fn alter_language(&mut self, language: HljsLang) -> &mut Self {
self.language = language;
self
}
#[fn_builder]
pub fn alter_snippet(&mut self, snippet: impl Into<String>) -> &mut Self {
self.snippet = snippet.into().trim().to_string();
self
}
// Hljs GETTERS.
pub fn language(&self) -> &HljsLang {
&self.language
}
pub fn snippet(&self) -> &String {
&self.snippet
}
}