summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/typst-cli/src/world.rs2
-rw-r--r--crates/typst-ide/Cargo.toml5
-rw-r--r--crates/typst-ide/src/complete.rs31
-rw-r--r--crates/typst-ide/src/lib.rs85
-rw-r--r--crates/typst-pdf/src/page.rs4
-rw-r--r--crates/typst-pdf/src/pattern.rs4
-rw-r--r--crates/typst-render/src/lib.rs23
-rw-r--r--crates/typst/src/model/footnote.rs11
8 files changed, 144 insertions, 21 deletions
diff --git a/crates/typst-cli/src/world.rs b/crates/typst-cli/src/world.rs
index 99da838c..5b4259f4 100644
--- a/crates/typst-cli/src/world.rs
+++ b/crates/typst-cli/src/world.rs
@@ -241,7 +241,7 @@ struct FileSlot {
}
impl FileSlot {
- /// Create a new path slot.
+ /// Create a new file slot.
fn new(id: FileId) -> Self {
Self { id, file: SlotCell::new(), source: SlotCell::new() }
}
diff --git a/crates/typst-ide/Cargo.toml b/crates/typst-ide/Cargo.toml
index 45a83d55..01f7a106 100644
--- a/crates/typst-ide/Cargo.toml
+++ b/crates/typst-ide/Cargo.toml
@@ -21,5 +21,10 @@ log = { workspace = true }
serde = { workspace = true }
unscanny = { workspace = true }
+[dev-dependencies]
+typst-assets = { workspace = true }
+typst-dev-assets = { workspace = true }
+once_cell = { workspace = true }
+
[lints]
workspace = true
diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs
index be28a431..c758dd1c 100644
--- a/crates/typst-ide/src/complete.rs
+++ b/crates/typst-ide/src/complete.rs
@@ -1403,3 +1403,34 @@ impl<'a> CompletionContext<'a> {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use typst::eval::Tracer;
+
+ use super::autocomplete;
+ use crate::tests::TestWorld;
+
+ #[track_caller]
+ fn test(text: &str, cursor: usize, contains: &[&str], excludes: &[&str]) {
+ let world = TestWorld::new(text);
+ let doc = typst::compile(&world, &mut Tracer::new()).ok();
+ let (_, completions) =
+ autocomplete(&world, doc.as_ref(), &world.main, cursor, true)
+ .unwrap_or_default();
+
+ let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect();
+ for item in contains {
+ assert!(labels.contains(item), "{item:?} was not contained in {labels:?}");
+ }
+ for item in excludes {
+ assert!(!labels.contains(item), "{item:?} was not excluded in {labels:?}");
+ }
+ }
+
+ #[test]
+ fn test_autocomplete() {
+ test("#i", 2, &["int", "if conditional"], &["foo"]);
+ test("#().", 4, &["insert", "remove", "len", "all"], &["foo"]);
+ }
+}
diff --git a/crates/typst-ide/src/lib.rs b/crates/typst-ide/src/lib.rs
index bbfd56d9..3967aaad 100644
--- a/crates/typst-ide/src/lib.rs
+++ b/crates/typst-ide/src/lib.rs
@@ -90,3 +90,88 @@ fn summarize_font_family<'a>(variants: impl Iterator<Item = &'a FontInfo>) -> Ec
detail
}
+
+#[cfg(test)]
+mod tests {
+ use comemo::Prehashed;
+ use once_cell::sync::Lazy;
+ use typst::diag::{FileError, FileResult};
+ use typst::foundations::{Bytes, Datetime};
+ use typst::syntax::{FileId, Source};
+ use typst::text::{Font, FontBook};
+ use typst::{Library, World};
+
+ /// A world for IDE testing.
+ pub struct TestWorld {
+ pub main: Source,
+ base: &'static TestBase,
+ }
+
+ impl TestWorld {
+ /// Create a new world for a single test.
+ ///
+ /// This is cheap because the shared base for all test runs is lazily
+ /// initialized just once.
+ pub fn new(text: &str) -> Self {
+ static BASE: Lazy<TestBase> = Lazy::new(TestBase::default);
+ let main = Source::detached(text);
+ Self { main, base: &*BASE }
+ }
+ }
+
+ impl World for TestWorld {
+ fn library(&self) -> &Prehashed<Library> {
+ &self.base.library
+ }
+
+ fn book(&self) -> &Prehashed<FontBook> {
+ &self.base.book
+ }
+
+ fn main(&self) -> Source {
+ self.main.clone()
+ }
+
+ fn source(&self, id: FileId) -> FileResult<Source> {
+ if id == self.main.id() {
+ Ok(self.main.clone())
+ } else {
+ Err(FileError::NotFound(id.vpath().as_rootless_path().into()))
+ }
+ }
+
+ fn file(&self, id: FileId) -> FileResult<Bytes> {
+ Err(FileError::NotFound(id.vpath().as_rootless_path().into()))
+ }
+
+ fn font(&self, index: usize) -> Option<Font> {
+ Some(self.base.fonts[index].clone())
+ }
+
+ fn today(&self, _: Option<i64>) -> Option<Datetime> {
+ None
+ }
+ }
+
+ /// Shared foundation of all test worlds.
+ struct TestBase {
+ library: Prehashed<Library>,
+ book: Prehashed<FontBook>,
+ fonts: Vec<Font>,
+ }
+
+ impl Default for TestBase {
+ fn default() -> Self {
+ let fonts: Vec<_> = typst_assets::fonts()
+ .chain(typst_dev_assets::fonts())
+ .flat_map(|data| Font::iter(Bytes::from_static(data)))
+ .collect();
+
+ Self {
+ library: Prehashed::new(Library::default()),
+ book: Prehashed::new(FontBook::from_fonts(&fonts)),
+ fonts,
+ }
+ }
+ }
+}
diff --git a/crates/typst-pdf/src/page.rs b/crates/typst-pdf/src/page.rs
index c31d1204..42358db5 100644
--- a/crates/typst-pdf/src/page.rs
+++ b/crates/typst-pdf/src/page.rs
@@ -377,7 +377,7 @@ pub struct EncodedPage {
}
/// Represents a resource being used in a PDF page by its name.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct PageResource {
kind: ResourceKind,
name: EcoString,
@@ -390,7 +390,7 @@ impl PageResource {
}
/// A kind of resource being used in a PDF page.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ResourceKind {
XObject,
Font,
diff --git a/crates/typst-pdf/src/pattern.rs b/crates/typst-pdf/src/pattern.rs
index 6dfb0f66..5d5942bc 100644
--- a/crates/typst-pdf/src/pattern.rs
+++ b/crates/typst-pdf/src/pattern.rs
@@ -118,13 +118,15 @@ fn register_pattern(
// Render the body.
let (_, content) = construct_page(ctx.parent, pattern.frame());
- let pdf_pattern = PdfPattern {
+ let mut pdf_pattern = PdfPattern {
transform,
pattern: pattern.clone(),
content: content.content.wait().clone(),
resources: content.resources.into_iter().collect(),
};
+ pdf_pattern.resources.sort();
+
ctx.parent.pattern_map.insert(pdf_pattern)
}
diff --git a/crates/typst-render/src/lib.rs b/crates/typst-render/src/lib.rs
index fdacb597..28302180 100644
--- a/crates/typst-render/src/lib.rs
+++ b/crates/typst-render/src/lib.rs
@@ -42,13 +42,13 @@ pub fn render(frame: &Frame, pixel_per_pt: f32, fill: Color) -> sk::Pixmap {
/// Export a document with potentially multiple pages into a single raster image.
///
-/// The padding will be added around and between the individual frames.
+/// The gap will be added between the individual frames.
pub fn render_merged(
document: &Document,
pixel_per_pt: f32,
frame_fill: Color,
- padding: Abs,
- padding_fill: Color,
+ gap: Abs,
+ gap_fill: Color,
) -> sk::Pixmap {
let pixmaps: Vec<_> = document
.pages
@@ -56,19 +56,18 @@ pub fn render_merged(
.map(|page| render(&page.frame, pixel_per_pt, frame_fill))
.collect();
- let padding = (pixel_per_pt * padding.to_f32()).round() as u32;
- let pxw =
- 2 * padding + pixmaps.iter().map(sk::Pixmap::width).max().unwrap_or_default();
- let pxh =
- padding + pixmaps.iter().map(|pixmap| pixmap.height() + padding).sum::<u32>();
+ let gap = (pixel_per_pt * gap.to_f32()).round() as u32;
+ let pxw = pixmaps.iter().map(sk::Pixmap::width).max().unwrap_or_default();
+ let pxh = pixmaps.iter().map(|pixmap| pixmap.height()).sum::<u32>()
+ + gap * pixmaps.len().saturating_sub(1) as u32;
let mut canvas = sk::Pixmap::new(pxw, pxh).unwrap();
- canvas.fill(to_sk_color(padding_fill));
+ canvas.fill(to_sk_color(gap_fill));
- let [x, mut y] = [padding; 2];
+ let mut y = 0;
for pixmap in pixmaps {
canvas.draw_pixmap(
- x as i32,
+ 0,
y as i32,
pixmap.as_ref(),
&sk::PixmapPaint::default(),
@@ -76,7 +75,7 @@ pub fn render_merged(
None,
);
- y += pixmap.height() + padding;
+ y += pixmap.height() + gap;
}
canvas
diff --git a/crates/typst/src/model/footnote.rs b/crates/typst/src/model/footnote.rs
index 44942341..4945ebb1 100644
--- a/crates/typst/src/model/footnote.rs
+++ b/crates/typst/src/model/footnote.rs
@@ -167,11 +167,6 @@ cast! {
/// This function is not intended to be called directly. Instead, it is used
/// in set and show rules to customize footnote listings.
///
-/// _Note:_ Set and show rules for `footnote.entry` must be defined at the
-/// beginning of the document in order to work correctly.
-/// See [here](https://github.com/typst/typst/issues/1348#issuecomment-1566316463)
-/// for more information.
-///
/// ```example
/// #show footnote.entry: set text(red)
///
@@ -179,6 +174,12 @@ cast! {
/// #footnote[It's down here]
/// has red text!
/// ```
+///
+/// _Note:_ Set and show rules for `footnote.entry` must be defined at the
+/// beginning of the document in order to work correctly. See [here][issue] for
+/// more information.
+///
+/// [issue]: https://github.com/typst/typst/issues/1467#issuecomment-1588799440
#[elem(name = "entry", title = "Footnote Entry", Show, ShowSet)]
pub struct FootnoteEntry {
/// The footnote for this entry. It's location can be used to determine