change file hashmap handling

add better error reporting
This commit is contained in:
Jack
2023-08-23 02:19:25 +01:00
parent 7045f5face
commit 12f9a0714f
7 changed files with 148 additions and 71 deletions

24
compiler/Cargo.lock generated
View File

@ -23,6 +23,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 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]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.7" version = "0.3.7"
@ -931,13 +941,13 @@ checksum = "e25be21376a772d15f97ae789845340a9651d3c4246ff5ebb6a2b35f9c37bd31"
name = "obsidian-typst" name = "obsidian-typst"
version = "0.5.1" version = "0.5.1"
dependencies = [ dependencies = [
"ariadne",
"comemo", "comemo",
"console_error_panic_hook", "console_error_panic_hook",
"fast_image_resize", "fast_image_resize",
"js-sys", "js-sys",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
"siphasher",
"typst", "typst",
"typst-library", "typst-library",
"wasm-bindgen", "wasm-bindgen",
@ -1926,6 +1936,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.4" version = "0.2.4"
@ -2192,6 +2208,12 @@ dependencies = [
"linked-hash-map", "linked-hash-map",
] ]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.7.1" version = "0.7.1"

View File

@ -15,7 +15,6 @@ crate-type = ["cdylib"]
typst = { git = "https://github.com/typst/typst.git", tag = "v0.7.0" } 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" } typst-library = { git = "https://github.com/typst/typst.git", tag = "v0.7.0" }
comemo = "0.3" comemo = "0.3"
siphasher = "0.3.10"
# Everything to do with wasm # Everything to do with wasm
@ -35,3 +34,5 @@ console_error_panic_hook = "0.1.7"
# Image handling # Image handling
fast_image_resize = "2.7.3" fast_image_resize = "2.7.3"
ariadne = "0.3.0"

View 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()
}
}

View File

@ -1,5 +1,6 @@
use comemo::Prehashed; use comemo::Prehashed;
use fast_image_resize as fr; use fast_image_resize as fr;
use render::format_diagnostic;
use std::{ use std::{
cell::{OnceCell, RefCell, RefMut}, cell::{OnceCell, RefCell, RefMut},
collections::HashMap, collections::HashMap,
@ -17,10 +18,10 @@ use typst::{
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use web_sys::ImageData; use web_sys::ImageData;
mod paths; mod file_entry;
mod render; mod render;
use crate::paths::{PathHash, PathSlot}; use crate::file_entry::FileEntry;
/// A world that provides access to the operating system. /// A world that provides access to the operating system.
#[wasm_bindgen] #[wasm_bindgen]
@ -35,12 +36,8 @@ pub struct SystemWorld {
book: Prehashed<FontBook>, book: Prehashed<FontBook>,
/// Storage of fonts /// Storage of fonts
fonts: Vec<Font>, 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 files: RefCell<HashMap<FileId, FileEntry>>,
/// 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>>,
/// The current date if requested. This is stored here to ensure it is /// The current date if requested. This is stored here to ensure it is
/// always the same within one compilation. Reset between compilations. /// always the same within one compilation. Reset between compilations.
today: OnceCell<Option<Datetime>>, today: OnceCell<Option<Datetime>>,
@ -66,8 +63,7 @@ impl SystemWorld {
library: Prehashed::new(typst_library::build()), library: Prehashed::new(typst_library::build()),
book: Prehashed::new(book), book: Prehashed::new(book),
fonts, fonts,
hashes: RefCell::default(), files: RefCell::default(),
paths: RefCell::default(),
today: OnceCell::new(), today: OnceCell::new(),
packages: RefCell::default(), packages: RefCell::default(),
resizer: fr::Resizer::default(), resizer: fr::Resizer::default(),
@ -77,6 +73,7 @@ impl SystemWorld {
pub fn compile( pub fn compile(
&mut self, &mut self,
// command: CompileCommand,
text: String, text: String,
path: String, path: String,
pixel_per_pt: f32, pixel_per_pt: f32,
@ -86,33 +83,24 @@ impl SystemWorld {
) -> Result<ImageData, JsValue> { ) -> Result<ImageData, JsValue> {
self.reset(); self.reset();
// Insert the main path slot self.main = FileId::new(None, &PathBuf::from(&path));
let system_path = PathBuf::from(path); self.files.borrow_mut().insert(
let hash = PathHash::new(&text); self.main,
self.main = FileId::new(None, &system_path); FileEntry::new(self.main, text), // bytes: OnceCell::new(),
self.hashes.borrow_mut().insert(self.main, Ok(hash)); // source: Source::new(self.main, text),
self.paths.borrow_mut().insert( // },
hash,
PathSlot {
id: self.main,
system_path,
buffer: OnceCell::new(),
source: Ok(Source::new(self.main, text)),
},
); );
let mut tracer = Tracer::default(); let mut tracer = Tracer::default();
match typst::compile(self, &mut tracer) { match typst::compile(self, &mut tracer) {
Ok(document) => { Ok(document) => render::to_image(
render::to_image(&mut self.resizer, document, size, display, fill, pixel_per_pt) &mut self.resizer,
} document,
Err(errors) => Err(format!( fill,
"{:?}", pixel_per_pt,
errors size,
.into_iter() display,
.map(|e| e.message) ),
.collect::<Vec<EcoString>>() Err(errors) => Err(format_diagnostic(self.files.borrow(), &errors).into()),
)
.into()),
} }
} }
@ -147,11 +135,11 @@ impl World for SystemWorld {
} }
fn source(&self, id: FileId) -> FileResult<Source> { fn source(&self, id: FileId) -> FileResult<Source> {
self.slot(id)?.source() Ok(self.file_entry(id)?.source())
} }
fn file(&self, id: FileId) -> FileResult<Bytes> { fn file(&self, id: FileId) -> FileResult<Bytes> {
self.slot(id)?.file() Ok(self.file_entry(id)?.bytes())
} }
fn font(&self, index: usize) -> Option<Font> { fn font(&self, index: usize) -> Option<Font> {
@ -165,8 +153,7 @@ impl World for SystemWorld {
impl SystemWorld { impl SystemWorld {
fn reset(&mut self) { fn reset(&mut self) {
self.hashes.borrow_mut().clear(); self.files.borrow_mut().clear();
self.paths.borrow_mut().clear();
self.today.take(); self.today.take();
} }
@ -207,33 +194,20 @@ impl SystemWorld {
.clone() .clone()
} }
fn slot(&self, id: FileId) -> FileResult<RefMut<PathSlot>> { fn file_entry(&self, id: FileId) -> FileResult<RefMut<FileEntry>> {
let mut system_path = PathBuf::new(); if let Ok(file) = RefMut::filter_map(self.files.borrow_mut(), |files| files.get_mut(&id)) {
let mut text = String::new(); return Ok(file);
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(),
};
system_path = root.join_rooted(id.path()).ok_or(FileError::AccessDenied)?; let path = match id.package() {
text = self.read_file(&system_path)?; Some(spec) => self.prepare_package(spec)?,
None => self.root.clone(),
Ok(PathHash::new(&text)) }
}) .join_rooted(id.path())
.clone()?; .ok_or(FileError::AccessDenied)?;
let text = self.read_file(&path)?;
Ok(RefMut::map(self.paths.borrow_mut(), |paths| { Ok(RefMut::map(self.files.borrow_mut(), |files| {
paths.entry(hash).or_insert_with(|| PathSlot { return files.entry(id).or_insert(FileEntry::new(id, text));
id,
source: Ok(Source::new(id, text)),
buffer: OnceCell::new(),
system_path,
})
})) }))
} }

View File

@ -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 fast_image_resize as fr;
use fr::Resizer; use fr::Resizer;
use typst::{ use typst::{
diag::{Severity, SourceDiagnostic},
doc::Document, doc::Document,
geom::{Color, RgbaColor}, geom::{Color, RgbaColor},
syntax::FileId,
}; };
use wasm_bindgen::Clamped; use wasm_bindgen::Clamped;
use web_sys::ImageData; use web_sys::ImageData;
use crate::file_entry::FileEntry;
pub fn to_image( pub fn to_image(
resizer: &mut Resizer, resizer: &mut Resizer,
document: Document, document: Document,
size: u32,
display: bool,
fill: String, fill: String,
pixel_per_pt: f32, pixel_per_pt: f32,
size: u32,
display: bool,
) -> Result<ImageData, wasm_bindgen::JsValue> { ) -> Result<ImageData, wasm_bindgen::JsValue> {
let mut pixmap = typst::export::render( let mut pixmap = typst::export::render(
&document.pages[0], &document.pages[0],
@ -69,3 +75,43 @@ pub fn to_image(
dst_height.get(), 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();
}

View File

@ -2,7 +2,8 @@
import wasmBin from '../pkg/obsidian_typst_bg.wasm' import wasmBin from '../pkg/obsidian_typst_bg.wasm'
import * as typst from '../pkg' import * as typst from '../pkg'
import { CompileCommand, WorkerRequest } from "src/types";
import { CompileCommand } from "src/types";
typst.initSync(wasmBin); typst.initSync(wasmBin);

View File

@ -65,7 +65,10 @@ export default class TypstCanvasElement extends HTMLCanvasElement {
await TypstCanvasElement.compile(this.path, this.source, this.size, this.display, fontSize) await TypstCanvasElement.compile(this.path, this.source, this.size, this.display, fontSize)
} catch (error) { } catch (error) {
console.error(error); console.error(error);
this.outerText = error let pre = createEl("pre")//"<pre> </pre>"
pre.textContent = error
this.outerHTML = pre.outerHTML
// this.innerText = error
return return
} }