mirror of
https://github.com/mii443/obsidian-typst.git
synced 2025-08-22 16:15:34 +00:00
change file hashmap handling
add better error reporting
This commit is contained in:
24
compiler/Cargo.lock
generated
24
compiler/Cargo.lock
generated
@ -23,6 +23,16 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.7"
|
||||
@ -931,13 +941,13 @@ checksum = "e25be21376a772d15f97ae789845340a9651d3c4246ff5ebb6a2b35f9c37bd31"
|
||||
name = "obsidian-typst"
|
||||
version = "0.5.1"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"comemo",
|
||||
"console_error_panic_hook",
|
||||
"fast_image_resize",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"siphasher",
|
||||
"typst",
|
||||
"typst-library",
|
||||
"wasm-bindgen",
|
||||
@ -1926,6 +1936,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
@ -2192,6 +2208,12 @@ dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.1"
|
||||
|
@ -15,7 +15,6 @@ crate-type = ["cdylib"]
|
||||
typst = { git = "https://github.com/typst/typst.git", tag = "v0.7.0" }
|
||||
typst-library = { git = "https://github.com/typst/typst.git", tag = "v0.7.0" }
|
||||
comemo = "0.3"
|
||||
siphasher = "0.3.10"
|
||||
|
||||
|
||||
# Everything to do with wasm
|
||||
@ -35,3 +34,5 @@ console_error_panic_hook = "0.1.7"
|
||||
|
||||
# Image handling
|
||||
fast_image_resize = "2.7.3"
|
||||
|
||||
ariadne = "0.3.0"
|
||||
|
30
compiler/src/file_entry.rs
Normal file
30
compiler/src/file_entry.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use std::cell::OnceCell;
|
||||
|
||||
use typst::{
|
||||
eval::Bytes,
|
||||
syntax::{FileId, Source},
|
||||
};
|
||||
|
||||
pub struct FileEntry {
|
||||
bytes: OnceCell<Bytes>,
|
||||
source: Source,
|
||||
}
|
||||
|
||||
impl FileEntry {
|
||||
pub fn new(id: FileId, text: String) -> Self {
|
||||
Self {
|
||||
bytes: OnceCell::new(),
|
||||
source: Source::new(id, text),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source(&self) -> Source {
|
||||
self.source.clone()
|
||||
}
|
||||
|
||||
pub fn bytes(&self) -> Bytes {
|
||||
self.bytes
|
||||
.get_or_init(|| Bytes::from(self.source.text().as_bytes()))
|
||||
.clone()
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
use comemo::Prehashed;
|
||||
use fast_image_resize as fr;
|
||||
use render::format_diagnostic;
|
||||
use std::{
|
||||
cell::{OnceCell, RefCell, RefMut},
|
||||
collections::HashMap,
|
||||
@ -17,10 +18,10 @@ use typst::{
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::ImageData;
|
||||
|
||||
mod paths;
|
||||
mod file_entry;
|
||||
mod render;
|
||||
|
||||
use crate::paths::{PathHash, PathSlot};
|
||||
use crate::file_entry::FileEntry;
|
||||
|
||||
/// A world that provides access to the operating system.
|
||||
#[wasm_bindgen]
|
||||
@ -35,12 +36,8 @@ pub struct SystemWorld {
|
||||
book: Prehashed<FontBook>,
|
||||
/// Storage of fonts
|
||||
fonts: Vec<Font>,
|
||||
/// Maps package-path combinations to canonical hashes. All package-path
|
||||
/// combinations that point to thes same file are mapped to the same hash. To
|
||||
/// be used in conjunction with `paths`.
|
||||
hashes: RefCell<HashMap<FileId, FileResult<PathHash>>>,
|
||||
/// Maps canonical path hashes to source files and buffers.
|
||||
paths: RefCell<HashMap<PathHash, PathSlot>>,
|
||||
|
||||
files: RefCell<HashMap<FileId, FileEntry>>,
|
||||
/// The current date if requested. This is stored here to ensure it is
|
||||
/// always the same within one compilation. Reset between compilations.
|
||||
today: OnceCell<Option<Datetime>>,
|
||||
@ -66,8 +63,7 @@ impl SystemWorld {
|
||||
library: Prehashed::new(typst_library::build()),
|
||||
book: Prehashed::new(book),
|
||||
fonts,
|
||||
hashes: RefCell::default(),
|
||||
paths: RefCell::default(),
|
||||
files: RefCell::default(),
|
||||
today: OnceCell::new(),
|
||||
packages: RefCell::default(),
|
||||
resizer: fr::Resizer::default(),
|
||||
@ -77,6 +73,7 @@ impl SystemWorld {
|
||||
|
||||
pub fn compile(
|
||||
&mut self,
|
||||
// command: CompileCommand,
|
||||
text: String,
|
||||
path: String,
|
||||
pixel_per_pt: f32,
|
||||
@ -86,33 +83,24 @@ impl SystemWorld {
|
||||
) -> Result<ImageData, JsValue> {
|
||||
self.reset();
|
||||
|
||||
// Insert the main path slot
|
||||
let system_path = PathBuf::from(path);
|
||||
let hash = PathHash::new(&text);
|
||||
self.main = FileId::new(None, &system_path);
|
||||
self.hashes.borrow_mut().insert(self.main, Ok(hash));
|
||||
self.paths.borrow_mut().insert(
|
||||
hash,
|
||||
PathSlot {
|
||||
id: self.main,
|
||||
system_path,
|
||||
buffer: OnceCell::new(),
|
||||
source: Ok(Source::new(self.main, text)),
|
||||
},
|
||||
self.main = FileId::new(None, &PathBuf::from(&path));
|
||||
self.files.borrow_mut().insert(
|
||||
self.main,
|
||||
FileEntry::new(self.main, text), // bytes: OnceCell::new(),
|
||||
// source: Source::new(self.main, text),
|
||||
// },
|
||||
);
|
||||
let mut tracer = Tracer::default();
|
||||
match typst::compile(self, &mut tracer) {
|
||||
Ok(document) => {
|
||||
render::to_image(&mut self.resizer, document, size, display, fill, pixel_per_pt)
|
||||
}
|
||||
Err(errors) => Err(format!(
|
||||
"{:?}",
|
||||
errors
|
||||
.into_iter()
|
||||
.map(|e| e.message)
|
||||
.collect::<Vec<EcoString>>()
|
||||
)
|
||||
.into()),
|
||||
Ok(document) => render::to_image(
|
||||
&mut self.resizer,
|
||||
document,
|
||||
fill,
|
||||
pixel_per_pt,
|
||||
size,
|
||||
display,
|
||||
),
|
||||
Err(errors) => Err(format_diagnostic(self.files.borrow(), &errors).into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,11 +135,11 @@ impl World for SystemWorld {
|
||||
}
|
||||
|
||||
fn source(&self, id: FileId) -> FileResult<Source> {
|
||||
self.slot(id)?.source()
|
||||
Ok(self.file_entry(id)?.source())
|
||||
}
|
||||
|
||||
fn file(&self, id: FileId) -> FileResult<Bytes> {
|
||||
self.slot(id)?.file()
|
||||
Ok(self.file_entry(id)?.bytes())
|
||||
}
|
||||
|
||||
fn font(&self, index: usize) -> Option<Font> {
|
||||
@ -165,8 +153,7 @@ impl World for SystemWorld {
|
||||
|
||||
impl SystemWorld {
|
||||
fn reset(&mut self) {
|
||||
self.hashes.borrow_mut().clear();
|
||||
self.paths.borrow_mut().clear();
|
||||
self.files.borrow_mut().clear();
|
||||
self.today.take();
|
||||
}
|
||||
|
||||
@ -207,33 +194,20 @@ impl SystemWorld {
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn slot(&self, id: FileId) -> FileResult<RefMut<PathSlot>> {
|
||||
let mut system_path = PathBuf::new();
|
||||
let mut text = String::new();
|
||||
let hash = self
|
||||
.hashes
|
||||
.borrow_mut()
|
||||
.entry(id)
|
||||
.or_insert_with(|| {
|
||||
let root = match id.package() {
|
||||
Some(spec) => self.prepare_package(spec)?,
|
||||
None => self.root.clone(),
|
||||
};
|
||||
fn file_entry(&self, id: FileId) -> FileResult<RefMut<FileEntry>> {
|
||||
if let Ok(file) = RefMut::filter_map(self.files.borrow_mut(), |files| files.get_mut(&id)) {
|
||||
return Ok(file);
|
||||
}
|
||||
|
||||
system_path = root.join_rooted(id.path()).ok_or(FileError::AccessDenied)?;
|
||||
text = self.read_file(&system_path)?;
|
||||
|
||||
Ok(PathHash::new(&text))
|
||||
})
|
||||
.clone()?;
|
||||
|
||||
Ok(RefMut::map(self.paths.borrow_mut(), |paths| {
|
||||
paths.entry(hash).or_insert_with(|| PathSlot {
|
||||
id,
|
||||
source: Ok(Source::new(id, text)),
|
||||
buffer: OnceCell::new(),
|
||||
system_path,
|
||||
})
|
||||
let path = match id.package() {
|
||||
Some(spec) => self.prepare_package(spec)?,
|
||||
None => self.root.clone(),
|
||||
}
|
||||
.join_rooted(id.path())
|
||||
.ok_or(FileError::AccessDenied)?;
|
||||
let text = self.read_file(&path)?;
|
||||
Ok(RefMut::map(self.files.borrow_mut(), |files| {
|
||||
return files.entry(id).or_insert(FileEntry::new(id, text));
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,27 @@
|
||||
use std::{num::NonZeroU32, str::FromStr};
|
||||
use std::{collections::HashMap, num::NonZeroU32, str::FromStr, cell::Ref};
|
||||
|
||||
use ariadne::{Config, FnCache, Label, Report, ReportKind, Source, Span};
|
||||
// use ariadne::{Report, ReportKind};
|
||||
use fast_image_resize as fr;
|
||||
use fr::Resizer;
|
||||
use typst::{
|
||||
diag::{Severity, SourceDiagnostic},
|
||||
doc::Document,
|
||||
geom::{Color, RgbaColor},
|
||||
syntax::FileId,
|
||||
};
|
||||
use wasm_bindgen::Clamped;
|
||||
use web_sys::ImageData;
|
||||
|
||||
use crate::file_entry::FileEntry;
|
||||
|
||||
pub fn to_image(
|
||||
resizer: &mut Resizer,
|
||||
document: Document,
|
||||
size: u32,
|
||||
display: bool,
|
||||
fill: String,
|
||||
pixel_per_pt: f32,
|
||||
size: u32,
|
||||
display: bool,
|
||||
) -> Result<ImageData, wasm_bindgen::JsValue> {
|
||||
let mut pixmap = typst::export::render(
|
||||
&document.pages[0],
|
||||
@ -69,3 +75,43 @@ pub fn to_image(
|
||||
dst_height.get(),
|
||||
);
|
||||
}
|
||||
|
||||
// impl Into<()> for FileId {
|
||||
|
||||
// }
|
||||
|
||||
pub fn format_diagnostic(
|
||||
sources: Ref<HashMap<FileId, FileEntry>>,
|
||||
diagnostics: &[SourceDiagnostic],
|
||||
) -> String {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
let cache = FnCache::new(|id| {
|
||||
return sources.get(&id);
|
||||
});
|
||||
|
||||
for diagnostic in diagnostics {
|
||||
let id = diagnostic.span.id();
|
||||
let source = sources.get(&id).unwrap().source();
|
||||
let range = source.range(diagnostic.span);
|
||||
let report = Report::build(
|
||||
match diagnostic.severity {
|
||||
Severity::Error => ReportKind::Error,
|
||||
Severity::Warning => ReportKind::Warning,
|
||||
},
|
||||
"arst",
|
||||
// id.path().to_str().unwrap(),
|
||||
range.start,
|
||||
)
|
||||
.with_config(Config::default().with_color(false).with_tab_width(2))
|
||||
.with_message(&diagnostic.message)
|
||||
.with_label(Label::new(range))
|
||||
.finish();
|
||||
report
|
||||
.write(Source::from(source.text()), &mut bytes)
|
||||
.unwrap();
|
||||
bytes.push(b'\n');
|
||||
}
|
||||
|
||||
return String::from_utf8(bytes).unwrap();
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
import wasmBin from '../pkg/obsidian_typst_bg.wasm'
|
||||
import * as typst from '../pkg'
|
||||
|
||||
import { CompileCommand, WorkerRequest } from "src/types";
|
||||
|
||||
import { CompileCommand } from "src/types";
|
||||
|
||||
typst.initSync(wasmBin);
|
||||
|
||||
|
@ -65,7 +65,10 @@ export default class TypstCanvasElement extends HTMLCanvasElement {
|
||||
await TypstCanvasElement.compile(this.path, this.source, this.size, this.display, fontSize)
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.outerText = error
|
||||
let pre = createEl("pre")//"<pre> </pre>"
|
||||
pre.textContent = error
|
||||
this.outerHTML = pre.outerHTML
|
||||
// this.innerText = error
|
||||
return
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user