Compare commits
11
Commits
0.1.3
..
c1057be18e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1057be18e | ||
|
|
06173555f6 | ||
|
|
308dcf2911 | ||
|
|
d5ecc05345 | ||
|
|
853efc406d | ||
|
|
035acb614f | ||
|
|
51173860be | ||
|
|
b157941d5e | ||
|
|
1efb44206b | ||
|
|
e193f96709 | ||
|
|
e6cc71fa47 |
No files matched your search
Generated
+795
-672
File diff suppressed because it is too large.
Load diff
+25
-15
@@ -1,20 +1,30 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "karnaugh"
|
name = "karnaugh"
|
||||||
version = "0.1.3"
|
version = "0.1.5"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.8.7"
|
axum = "0.8.9"
|
||||||
flate2 = "1.1.5"
|
flate2 = "1.1.9"
|
||||||
itertools = "0.14.0"
|
itertools = "0.15.0"
|
||||||
tar = "0.4.44"
|
tar = "0.4.46"
|
||||||
time = "0.3.47"
|
time = "0.3.53"
|
||||||
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.53.0", features = ["macros", "rt-multi-thread"] }
|
||||||
tower-http = { version = "0.6.8", features = ["compression-br", "fs", "trace"] }
|
tower-http = { version = "0.7.0", features = [
|
||||||
tracing = "0.1.43"
|
"compression-br",
|
||||||
tracing-subscriber = "0.3.22"
|
"fs",
|
||||||
typst-html = { git = "https://github.com/mkorje/typst.git", branch = "mathml" }
|
"trace",
|
||||||
typst-kit = { git = "https://github.com/mkorje/typst.git", branch = "mathml" }
|
"set-header",
|
||||||
typst = { git = "https://github.com/mkorje/typst.git", branch = "mathml" }
|
] }
|
||||||
ureq = "3.1.4"
|
tracing = "0.1.44"
|
||||||
clap = { version = "4.6.0", features = ["derive"] }
|
tracing-subscriber = "0.3.23"
|
||||||
|
typst-html = "0.15.1"
|
||||||
|
typst-kit = "0.15.1"
|
||||||
|
typst = "0.15.1"
|
||||||
|
ureq = "3.3.0"
|
||||||
|
clap = { version = "4.6.2", features = ["derive"] }
|
||||||
|
typst-library = "0.15.1"
|
||||||
|
typst-utils = "0.15.1"
|
||||||
|
tree-house = "0.4.0"
|
||||||
|
tree-house-bindings = { version = "0.3.2", features = ["ropey"] }
|
||||||
|
ropey = "1.6.1"
|
||||||
Generated
+61
-4
@@ -18,13 +18,48 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"helix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1784219418,
|
||||||
|
"narHash": "sha256-yfNBfV6HWAQU/xZXwrFoedsSYsDmCDIlTHRxAZI6teY=",
|
||||||
|
"owner": "helix-editor",
|
||||||
|
"repo": "helix",
|
||||||
|
"rev": "f6f3eb1fe4a73933466f30175843059a4381a985",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "helix-editor",
|
||||||
|
"repo": "helix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773821835,
|
"lastModified": 1775036866,
|
||||||
"narHash": "sha256-TJ3lSQtW0E2JrznGVm8hOQGVpXjJyXY2guAxku2O9A4=",
|
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b40629efe5d6ec48dd1efba650c797ddbd39ace0",
|
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1784120854,
|
||||||
|
"narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -37,7 +72,29 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs"
|
"helix": "helix",
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"helix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775358767,
|
||||||
|
"narHash": "sha256-f2eC+WIfhjevCPQILuV08i/kmKZzYZpUvkom/33VxCA=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "20fd44bc663daa53a2575e01293e24e681d62244",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
helix.url = "github:helix-editor/helix";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
flake-utils,
|
flake-utils,
|
||||||
|
helix,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
flake-utils.lib.eachDefaultSystem (
|
flake-utils.lib.eachDefaultSystem (
|
||||||
@@ -19,11 +21,36 @@
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
|
packages.syntax =
|
||||||
|
let
|
||||||
|
grammars = pkgs.callPackage "${helix}/grammars.nix" { };
|
||||||
|
in
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "syntax";
|
||||||
|
phases = [
|
||||||
|
"buildPhase"
|
||||||
|
"installPhase"
|
||||||
|
];
|
||||||
|
buildPhase = ''
|
||||||
|
cp -r ${helix}/runtime/queries/* ./
|
||||||
|
chmod u+w ./* -R
|
||||||
|
for so in ${grammars}/*.so; do
|
||||||
|
name=$(basename "$so" .so)
|
||||||
|
if [ -d "./$name" ]; then
|
||||||
|
cp "$so" "./$name/$name.so"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r ./* $out/
|
||||||
|
'';
|
||||||
|
};
|
||||||
packages.karnaugh = pkgs.rustPlatform.buildRustPackage (final: {
|
packages.karnaugh = pkgs.rustPlatform.buildRustPackage (final: {
|
||||||
pname = "karnaugh";
|
pname = "karnaugh";
|
||||||
version = "0.1.3";
|
version = "0.1.5";
|
||||||
src = self;
|
src = self;
|
||||||
cargoHash = "sha256-HwwvbYf9hKBANbIoxBQFOgya3K7SwWZCPZKvLGB057U=";
|
cargoLock.lockFile = ./Cargo.lock;
|
||||||
});
|
});
|
||||||
packages.container = pkgs.dockerTools.buildImage {
|
packages.container = pkgs.dockerTools.buildImage {
|
||||||
name = "karnaugh";
|
name = "karnaugh";
|
||||||
@@ -47,6 +74,8 @@
|
|||||||
rustc
|
rustc
|
||||||
rustfmt
|
rustfmt
|
||||||
clippy
|
clippy
|
||||||
|
cargo-edit
|
||||||
|
cargo-audit
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-4
@@ -8,7 +8,7 @@ use ::typst::syntax::VirtualPath;
|
|||||||
use axum::{
|
use axum::{
|
||||||
Router,
|
Router,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::StatusCode,
|
http::{HeaderValue, StatusCode, header::CACHE_CONTROL},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
};
|
};
|
||||||
@@ -17,6 +17,7 @@ use itertools::Itertools;
|
|||||||
use tower_http::{
|
use tower_http::{
|
||||||
compression::CompressionLayer,
|
compression::CompressionLayer,
|
||||||
services::{ServeDir, ServeFile},
|
services::{ServeDir, ServeFile},
|
||||||
|
set_header::SetResponseHeaderLayer,
|
||||||
trace::TraceLayer,
|
trace::TraceLayer,
|
||||||
};
|
};
|
||||||
use tracing::{Level, info};
|
use tracing::{Level, info};
|
||||||
@@ -24,6 +25,7 @@ use tracing_subscriber::FmtSubscriber;
|
|||||||
|
|
||||||
use crate::typst::TypstContext;
|
use crate::typst::TypstContext;
|
||||||
|
|
||||||
|
mod syntax;
|
||||||
mod typst;
|
mod typst;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -58,6 +60,15 @@ struct AppConfig {
|
|||||||
#[arg(short = 'd', long, value_name = "DIR", default_value = "/tmp/karnaugh")]
|
#[arg(short = 'd', long, value_name = "DIR", default_value = "/tmp/karnaugh")]
|
||||||
packages_root: PathBuf,
|
packages_root: PathBuf,
|
||||||
|
|
||||||
|
/// The directory where the syntax files are stored.
|
||||||
|
#[arg(short = 'g', long, value_name = "DIR", default_value = None)]
|
||||||
|
syntax_root: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Grammars to load, separated by commas.
|
||||||
|
/// Useful to avoid the memory required to load all languages.
|
||||||
|
#[arg(short = 'L', long = "lang", value_name = "NAME", value_delimiter = ',')]
|
||||||
|
languages: Vec<String>,
|
||||||
|
|
||||||
/// The log level to use
|
/// The log level to use
|
||||||
#[arg(short = 'l', long, value_name = "LEVEL", default_value_t = Level::INFO)]
|
#[arg(short = 'l', long, value_name = "LEVEL", default_value_t = Level::INFO)]
|
||||||
log_level: Level,
|
log_level: Level,
|
||||||
@@ -93,24 +104,32 @@ async fn main() {
|
|||||||
.with_max_level(state.config.log_level)
|
.with_max_level(state.config.log_level)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
syntax::init(&state.config);
|
||||||
|
|
||||||
// TODO: Replace with config option
|
// TODO: Replace with config option
|
||||||
let mut favicon = state.config.get_full_assets_path();
|
let mut favicon = state.config.get_full_assets_path();
|
||||||
favicon.push(PathBuf::from("favicon.svg"));
|
favicon.push(PathBuf::from("favicon.svg"));
|
||||||
|
|
||||||
|
let assets_service = ServeDir::new(state.config.get_full_assets_path());
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(root_handler))
|
.route("/", get(root_handler))
|
||||||
.route("/{*path}", get(typst_handler))
|
.route("/{*path}", get(typst_handler))
|
||||||
.route_service("/favicon.ico", ServeFile::new(favicon))
|
.route_service("/favicon.ico", ServeFile::new(favicon))
|
||||||
.nest_service("/assets", ServeDir::new(&state.config.assets_root))
|
.nest_service("/assets", assets_service)
|
||||||
.with_state(state.clone())
|
.with_state(state.clone())
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
.layer(CompressionLayer::new());
|
.layer(CompressionLayer::new())
|
||||||
|
.layer(SetResponseHeaderLayer::overriding(
|
||||||
|
CACHE_CONTROL,
|
||||||
|
HeaderValue::from_static("public, max-age=86400, immutable"),
|
||||||
|
));
|
||||||
|
|
||||||
let socket = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), state.config.port);
|
let socket = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), state.config.port);
|
||||||
info!("Serving Karnaugh on socket {}", socket);
|
info!("Serving Karnaugh on socket {}", socket);
|
||||||
let listener = tokio::net::TcpListener::bind(socket)
|
let listener = tokio::net::TcpListener::bind(socket)
|
||||||
.await
|
.await
|
||||||
.expect(&format!("Failed to bind to socket {}", socket));
|
.unwrap_or_else(|_| panic!("Failed to bind to socket {}", socket));
|
||||||
axum::serve(listener, app)
|
axum::serve(listener, app)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to serve site");
|
.expect("Failed to serve site");
|
||||||
|
|||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
error::Error,
|
||||||
|
fs,
|
||||||
|
path::Path,
|
||||||
|
sync::OnceLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use tracing::{info, warn};
|
||||||
|
use tree_house::{
|
||||||
|
InjectionLanguageMarker, Language, LanguageConfig, LanguageLoader, highlighter::Highlight,
|
||||||
|
read_query,
|
||||||
|
};
|
||||||
|
use tree_house_bindings::Grammar;
|
||||||
|
use typst::foundations::{Func, NativeFunc, func};
|
||||||
|
|
||||||
|
use crate::AppConfig;
|
||||||
|
|
||||||
|
static REGISTRY: OnceLock<LanguageRegistry> = OnceLock::new();
|
||||||
|
|
||||||
|
/// Class names used in highlighted code.
|
||||||
|
const HIGHLIGHT_NAMES: &[&str] = &[
|
||||||
|
"attribute",
|
||||||
|
"comment",
|
||||||
|
"comment.block",
|
||||||
|
"comment.line",
|
||||||
|
"constant",
|
||||||
|
"constant.builtin",
|
||||||
|
"constructor",
|
||||||
|
"function",
|
||||||
|
"function.builtin",
|
||||||
|
"function.macro",
|
||||||
|
"function.method",
|
||||||
|
"keyword",
|
||||||
|
"keyword.control",
|
||||||
|
"keyword.operator",
|
||||||
|
"label",
|
||||||
|
"namespace",
|
||||||
|
"number",
|
||||||
|
"operator",
|
||||||
|
"property",
|
||||||
|
"punctuation",
|
||||||
|
"punctuation.bracket",
|
||||||
|
"punctuation.delimiter",
|
||||||
|
"special",
|
||||||
|
"string",
|
||||||
|
"string.special",
|
||||||
|
"tag",
|
||||||
|
"type",
|
||||||
|
"type.builtin",
|
||||||
|
"variable",
|
||||||
|
"variable.builtin",
|
||||||
|
"variable.parameter",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Get the longest matching highlight name for a given scope.
|
||||||
|
fn highlight_for_scope(scope: &str) -> Option<Highlight> {
|
||||||
|
let mut candidate = scope;
|
||||||
|
loop {
|
||||||
|
if let Some(i) = HIGHLIGHT_NAMES.iter().position(|&n| n == candidate) {
|
||||||
|
return Some(Highlight::new(i as u32));
|
||||||
|
}
|
||||||
|
match candidate.rfind('.') {
|
||||||
|
Some(dot) => candidate = &candidate[..dot],
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LanguageRegistry {
|
||||||
|
by_name: HashMap<String, Language>,
|
||||||
|
configs: Vec<LanguageConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageRegistry {
|
||||||
|
/// Loads grammars from `syntax_root`. When `languages` is non-empty, only
|
||||||
|
/// grammars with those names are loaded; otherwise every grammar found is.
|
||||||
|
fn new(syntax_root: &Path, languages: &[String]) -> Self {
|
||||||
|
let filter: Option<HashSet<&str>> =
|
||||||
|
(!languages.is_empty()).then(|| languages.iter().map(String::as_str).collect());
|
||||||
|
|
||||||
|
let mut by_name = HashMap::new();
|
||||||
|
let mut configs = Vec::new();
|
||||||
|
|
||||||
|
for entry in fs::read_dir(syntax_root).into_iter().flatten().flatten() {
|
||||||
|
let name = entry.file_name().to_string_lossy().into_owned();
|
||||||
|
if filter.as_ref().is_some_and(|f| !f.contains(name.as_str())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let so_path = entry.path().join(format!("{name}.so"));
|
||||||
|
if !so_path.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match load_language(&name, &entry.path(), syntax_root) {
|
||||||
|
Ok(config) => {
|
||||||
|
by_name.insert(name, Language::new(configs.len() as u32));
|
||||||
|
configs.push(config);
|
||||||
|
}
|
||||||
|
Err(e) => warn!("Failed to load grammar {name}: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn about explicitly requested grammars that were not found.
|
||||||
|
for lang in languages {
|
||||||
|
if !by_name.contains_key(lang) {
|
||||||
|
warn!("Requested grammar {lang} was not found in the syntax root");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Loaded {} syntax highlighting grammars", configs.len());
|
||||||
|
Self { by_name, configs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_language(
|
||||||
|
name: &str,
|
||||||
|
lang_dir: &Path,
|
||||||
|
syntax_root: &Path,
|
||||||
|
) -> Result<LanguageConfig, Box<dyn Error>> {
|
||||||
|
let grammar = unsafe { Grammar::new(name, &lang_dir.join(format!("{name}.so")))? };
|
||||||
|
|
||||||
|
let read = |kind: &str| {
|
||||||
|
read_query(name, |lang| {
|
||||||
|
fs::read_to_string(syntax_root.join(lang).join(format!("{kind}.scm")))
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let config = LanguageConfig::new(
|
||||||
|
grammar,
|
||||||
|
&read("highlights"),
|
||||||
|
&read("injections"),
|
||||||
|
&read("locals"),
|
||||||
|
)?;
|
||||||
|
config.configure(highlight_for_scope);
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageLoader for LanguageRegistry {
|
||||||
|
fn language_for_marker(&self, marker: InjectionLanguageMarker<'_>) -> Option<Language> {
|
||||||
|
match marker {
|
||||||
|
InjectionLanguageMarker::Name(name) => self.by_name.get(name).copied(),
|
||||||
|
InjectionLanguageMarker::Match(text) => {
|
||||||
|
let name: Cow<str> = text.into();
|
||||||
|
self.by_name.get(name.as_ref()).copied()
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_config(&self, lang: Language) -> Option<&LanguageConfig> {
|
||||||
|
self.configs.get(lang.idx())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(config: &AppConfig) {
|
||||||
|
match &config.syntax_root {
|
||||||
|
Some(syntax_root) => {
|
||||||
|
info!("Loading syntax files from {}", syntax_root.display());
|
||||||
|
REGISTRY.get_or_init(|| LanguageRegistry::new(syntax_root, &config.languages));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warn!(
|
||||||
|
"No syntax root has been configured, improved tree-sitter highlighting will not be possible."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn highlight_func() -> Func {
|
||||||
|
highlight::func()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[func]
|
||||||
|
fn highlight(
|
||||||
|
text: String,
|
||||||
|
#[named]
|
||||||
|
#[default(false)]
|
||||||
|
block: bool,
|
||||||
|
#[named]
|
||||||
|
#[default(None)]
|
||||||
|
lang: Option<String>,
|
||||||
|
) -> String {
|
||||||
|
let rev = text.chars().rev();
|
||||||
|
rev.collect()
|
||||||
|
}
|
||||||
@@ -5,11 +5,12 @@ use tracing::info;
|
|||||||
use typst::{
|
use typst::{
|
||||||
Library, LibraryExt, World,
|
Library, LibraryExt, World,
|
||||||
diag::{FileError, FileResult, PackageError, PackageResult, SourceDiagnostic},
|
diag::{FileError, FileResult, PackageError, PackageResult, SourceDiagnostic},
|
||||||
foundations::{Bytes, Datetime, Duration},
|
foundations::{Bytes, Datetime, Dict, Duration, Value},
|
||||||
syntax::{FileId, RootedPath, Source, VirtualPath, VirtualRoot, package::PackageSpec},
|
syntax::{FileId, RootedPath, Source, VirtualPath, VirtualRoot, package::PackageSpec},
|
||||||
text::{Font, FontBook},
|
text::{Font, FontBook},
|
||||||
utils::LazyHash,
|
utils::LazyHash,
|
||||||
};
|
};
|
||||||
|
use typst_html::HtmlOptions;
|
||||||
use typst_kit::fonts::FontStore;
|
use typst_kit::fonts::FontStore;
|
||||||
|
|
||||||
use crate::AppConfig;
|
use crate::AppConfig;
|
||||||
@@ -22,7 +23,15 @@ pub struct TypstContext {
|
|||||||
|
|
||||||
impl TypstContext {
|
impl TypstContext {
|
||||||
pub fn new(config: AppConfig) -> Self {
|
pub fn new(config: AppConfig) -> Self {
|
||||||
|
let mut inputs = Dict::new();
|
||||||
|
if config.syntax_root.is_some() {
|
||||||
|
inputs.insert(
|
||||||
|
"highlight".into(),
|
||||||
|
Value::Func(crate::syntax::highlight_func()),
|
||||||
|
);
|
||||||
|
}
|
||||||
let library = Library::builder()
|
let library = Library::builder()
|
||||||
|
.with_inputs(inputs)
|
||||||
.with_features([typst::Feature::Html].into_iter().collect())
|
.with_features([typst::Feature::Html].into_iter().collect())
|
||||||
.build();
|
.build();
|
||||||
let library = LazyHash::new(library);
|
let library = LazyHash::new(library);
|
||||||
@@ -41,12 +50,14 @@ impl TypstContext {
|
|||||||
let world = DocumentWorld::new(path, self);
|
let world = DocumentWorld::new(path, self);
|
||||||
let compiled = typst::compile(&world);
|
let compiled = typst::compile(&world);
|
||||||
|
|
||||||
|
let html_options = HtmlOptions::default();
|
||||||
|
|
||||||
// TODO: Log warnings
|
// TODO: Log warnings
|
||||||
|
|
||||||
// TODO: Log errors
|
// TODO: Log errors
|
||||||
compiled
|
compiled
|
||||||
.output
|
.output
|
||||||
.and_then(|doc| typst_html::html(&doc))
|
.and_then(|doc| typst_html::html(&doc, &html_options))
|
||||||
.map_err(|err| err.into_iter().collect())
|
.map_err(|err| err.into_iter().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +127,8 @@ impl<'a> DocumentWorld<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_project_file(&self, path: &VirtualPath) -> FileResult<PathBuf> {
|
fn resolve_project_file(&self, path: &VirtualPath) -> FileResult<PathBuf> {
|
||||||
Ok(path.realize(&self.context.config.content_root))
|
path.realize(&self.context.config.content_root)
|
||||||
|
.map_err(FileError::Realize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_package_file(&self, path: &VirtualPath, spec: &PackageSpec) -> FileResult<PathBuf> {
|
fn resolve_package_file(&self, path: &VirtualPath, spec: &PackageSpec) -> FileResult<PathBuf> {
|
||||||
@@ -129,12 +141,13 @@ impl<'a> DocumentWorld<'a> {
|
|||||||
|
|
||||||
fn resolve_common_file(&self, path: &VirtualPath, spec: &PackageSpec) -> FileResult<PathBuf> {
|
fn resolve_common_file(&self, path: &VirtualPath, spec: &PackageSpec) -> FileResult<PathBuf> {
|
||||||
let path = get_package_path(path, spec);
|
let path = get_package_path(path, spec);
|
||||||
Ok(path.realize(&self.context.config.get_full_common_path()))
|
path.realize(&self.context.config.get_full_common_path())
|
||||||
|
.map_err(FileError::Realize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_preview_file(&self, path: &VirtualPath, spec: &PackageSpec) -> FileResult<PathBuf> {
|
fn resolve_preview_file(&self, path: &VirtualPath, spec: &PackageSpec) -> FileResult<PathBuf> {
|
||||||
let package_path = self.get_package(spec)?;
|
let package_path = self.get_package(spec)?;
|
||||||
Ok(path.realize(&package_path))
|
path.realize(&package_path).map_err(FileError::Realize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_package(&self, spec: &PackageSpec) -> PackageResult<PathBuf> {
|
fn get_package(&self, spec: &PackageSpec) -> PackageResult<PathBuf> {
|
||||||
@@ -142,7 +155,9 @@ impl<'a> DocumentWorld<'a> {
|
|||||||
&VirtualPath::new("").expect("Failed to create empty virtual path"),
|
&VirtualPath::new("").expect("Failed to create empty virtual path"),
|
||||||
spec,
|
spec,
|
||||||
);
|
);
|
||||||
let path = path.realize(&self.context.config.packages_root);
|
let path = path
|
||||||
|
.realize(&self.context.config.packages_root)
|
||||||
|
.map_err(|_| PackageError::NotFound(spec.clone()))?;
|
||||||
|
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
return Ok(path);
|
return Ok(path);
|
||||||
Reference in new issue
Block a user