mirror of
https://github.com/typst/typst
synced 2025-08-12 06:07:54 +08:00
Compare commits
6 Commits
8dce676dcd
...
4a78a7d082
Author | SHA1 | Date | |
---|---|---|---|
|
4a78a7d082 | ||
|
a754be513d | ||
|
7d4010afad | ||
|
4893eb501e | ||
|
20d4f8135a | ||
|
a998775edc |
@ -204,6 +204,10 @@ impl Watcher {
|
||||
let event = event
|
||||
.map_err(|err| eco_format!("failed to watch dependencies ({err})"))?;
|
||||
|
||||
if !is_relevant_event_kind(&event.kind) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Workaround for notify-rs' implicit unwatch on remove/rename
|
||||
// (triggered by some editors when saving files) with the
|
||||
// inotify backend. By keeping track of the potentially
|
||||
@ -224,7 +228,17 @@ impl Watcher {
|
||||
}
|
||||
}
|
||||
|
||||
relevant |= self.is_event_relevant(&event);
|
||||
// Don't recompile because the output file changed.
|
||||
// FIXME: This doesn't work properly for multifile image export.
|
||||
if event
|
||||
.paths
|
||||
.iter()
|
||||
.all(|path| is_same_file(path, &self.output).unwrap_or(false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
relevant = true;
|
||||
}
|
||||
|
||||
// If we found a relevant event or if any of the missing files now
|
||||
@ -234,32 +248,23 @@ impl Watcher {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a watch event is relevant for compilation.
|
||||
fn is_event_relevant(&self, event: ¬ify::Event) -> bool {
|
||||
// Never recompile because the output file changed.
|
||||
if event
|
||||
.paths
|
||||
.iter()
|
||||
.all(|path| is_same_file(path, &self.output).unwrap_or(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
match &event.kind {
|
||||
notify::EventKind::Any => true,
|
||||
notify::EventKind::Access(_) => false,
|
||||
notify::EventKind::Create(_) => true,
|
||||
notify::EventKind::Modify(kind) => match kind {
|
||||
notify::event::ModifyKind::Any => true,
|
||||
notify::event::ModifyKind::Data(_) => true,
|
||||
notify::event::ModifyKind::Metadata(_) => false,
|
||||
notify::event::ModifyKind::Name(_) => true,
|
||||
notify::event::ModifyKind::Other => false,
|
||||
},
|
||||
notify::EventKind::Remove(_) => true,
|
||||
notify::EventKind::Other => false,
|
||||
}
|
||||
/// Whether a kind of watch event is relevant for compilation.
|
||||
fn is_relevant_event_kind(kind: ¬ify::EventKind) -> bool {
|
||||
match kind {
|
||||
notify::EventKind::Any => true,
|
||||
notify::EventKind::Access(_) => false,
|
||||
notify::EventKind::Create(_) => true,
|
||||
notify::EventKind::Modify(kind) => match kind {
|
||||
notify::event::ModifyKind::Any => true,
|
||||
notify::event::ModifyKind::Data(_) => true,
|
||||
notify::event::ModifyKind::Metadata(_) => false,
|
||||
notify::event::ModifyKind::Name(_) => true,
|
||||
notify::event::ModifyKind::Other => false,
|
||||
},
|
||||
notify::EventKind::Remove(_) => true,
|
||||
notify::EventKind::Other => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,8 +83,8 @@ fn html_document_impl(
|
||||
)?;
|
||||
|
||||
let output = handle_list(&mut engine, &mut locator, children.iter().copied())?;
|
||||
let introspector = Introspector::html(&output);
|
||||
let root = root_element(output, &info)?;
|
||||
let introspector = Introspector::html(&root);
|
||||
|
||||
Ok(HtmlDocument { info, root, introspector })
|
||||
}
|
||||
@ -307,18 +307,18 @@ fn head_element(info: &DocumentInfo) -> HtmlElement {
|
||||
|
||||
/// Determine which kind of output the user generated.
|
||||
fn classify_output(mut output: Vec<HtmlNode>) -> SourceResult<OutputKind> {
|
||||
let len = output.len();
|
||||
let count = output.iter().filter(|node| !matches!(node, HtmlNode::Tag(_))).count();
|
||||
for node in &mut output {
|
||||
let HtmlNode::Element(elem) = node else { continue };
|
||||
let tag = elem.tag;
|
||||
let mut take = || std::mem::replace(elem, HtmlElement::new(tag::html));
|
||||
match (tag, len) {
|
||||
match (tag, count) {
|
||||
(tag::html, 1) => return Ok(OutputKind::Html(take())),
|
||||
(tag::body, 1) => return Ok(OutputKind::Body(take())),
|
||||
(tag::html | tag::body, _) => bail!(
|
||||
elem.span,
|
||||
"`{}` element must be the only element in the document",
|
||||
elem.tag
|
||||
elem.tag,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
|
@ -437,10 +437,10 @@ impl PartialEq for Func {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&NativeFuncData> for Func {
|
||||
fn eq(&self, other: &&NativeFuncData) -> bool {
|
||||
impl PartialEq<&'static NativeFuncData> for Func {
|
||||
fn eq(&self, other: &&'static NativeFuncData) -> bool {
|
||||
match &self.repr {
|
||||
Repr::Native(native) => native.function == other.function,
|
||||
Repr::Native(native) => *native == Static(*other),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -498,7 +498,7 @@ pub fn equal(lhs: &Value, rhs: &Value, sink: &mut dyn DeprecationSink) -> bool {
|
||||
|
||||
// Type compatibility.
|
||||
(Type(ty), Str(str)) | (Str(str), Type(ty)) => {
|
||||
warn_type_str_equal(sink);
|
||||
warn_type_str_equal(sink, str);
|
||||
ty.compat_name() == str.as_str()
|
||||
}
|
||||
|
||||
@ -647,14 +647,17 @@ fn warn_type_str_join(sink: &mut dyn DeprecationSink) {
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn warn_type_str_equal(sink: &mut dyn DeprecationSink) {
|
||||
sink.emit_with_hints(
|
||||
"comparing strings with types is deprecated",
|
||||
&[
|
||||
"compare with the literal type instead",
|
||||
"this comparison will always return `false` in future Typst releases",
|
||||
],
|
||||
);
|
||||
fn warn_type_str_equal(sink: &mut dyn DeprecationSink, s: &str) {
|
||||
// Only warn if `s` looks like a type name to prevent false positives.
|
||||
if is_compat_type_name(s) {
|
||||
sink.emit_with_hints(
|
||||
"comparing strings with types is deprecated",
|
||||
&[
|
||||
"compare with the literal type instead",
|
||||
"this comparison will always return `false` in future Typst releases",
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
@ -672,3 +675,44 @@ fn warn_type_in_dict(sink: &mut dyn DeprecationSink) {
|
||||
&["this compatibility behavior only exists because `type` used to return a string"],
|
||||
);
|
||||
}
|
||||
|
||||
fn is_compat_type_name(s: &str) -> bool {
|
||||
matches!(
|
||||
s,
|
||||
"boolean"
|
||||
| "alignment"
|
||||
| "angle"
|
||||
| "arguments"
|
||||
| "array"
|
||||
| "bytes"
|
||||
| "color"
|
||||
| "content"
|
||||
| "counter"
|
||||
| "datetime"
|
||||
| "decimal"
|
||||
| "dictionary"
|
||||
| "direction"
|
||||
| "duration"
|
||||
| "float"
|
||||
| "fraction"
|
||||
| "function"
|
||||
| "gradient"
|
||||
| "integer"
|
||||
| "label"
|
||||
| "length"
|
||||
| "location"
|
||||
| "module"
|
||||
| "pattern"
|
||||
| "ratio"
|
||||
| "regex"
|
||||
| "relative length"
|
||||
| "selector"
|
||||
| "state"
|
||||
| "string"
|
||||
| "stroke"
|
||||
| "symbol"
|
||||
| "tiling"
|
||||
| "type"
|
||||
| "version"
|
||||
)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use typst_utils::NonZeroExt;
|
||||
|
||||
use crate::diag::{bail, StrResult};
|
||||
use crate::foundations::{Content, Label, Repr, Selector};
|
||||
use crate::html::{HtmlElement, HtmlNode};
|
||||
use crate::html::HtmlNode;
|
||||
use crate::introspection::{Location, Tag};
|
||||
use crate::layout::{Frame, FrameItem, Page, Point, Position, Transform};
|
||||
use crate::model::Numbering;
|
||||
@ -55,8 +55,8 @@ impl Introspector {
|
||||
|
||||
/// Creates an introspector for HTML.
|
||||
#[typst_macros::time(name = "introspect html")]
|
||||
pub fn html(root: &HtmlElement) -> Self {
|
||||
IntrospectorBuilder::new().build_html(root)
|
||||
pub fn html(output: &[HtmlNode]) -> Self {
|
||||
IntrospectorBuilder::new().build_html(output)
|
||||
}
|
||||
|
||||
/// Iterates over all locatable elements.
|
||||
@ -392,9 +392,9 @@ impl IntrospectorBuilder {
|
||||
}
|
||||
|
||||
/// Build an introspector for an HTML document.
|
||||
fn build_html(mut self, root: &HtmlElement) -> Introspector {
|
||||
fn build_html(mut self, output: &[HtmlNode]) -> Introspector {
|
||||
let mut elems = Vec::new();
|
||||
self.discover_in_html(&mut elems, root);
|
||||
self.discover_in_html(&mut elems, output);
|
||||
self.finalize(elems)
|
||||
}
|
||||
|
||||
@ -434,16 +434,16 @@ impl IntrospectorBuilder {
|
||||
}
|
||||
|
||||
/// Processes the tags in the HTML element.
|
||||
fn discover_in_html(&mut self, sink: &mut Vec<Pair>, elem: &HtmlElement) {
|
||||
for child in &elem.children {
|
||||
match child {
|
||||
fn discover_in_html(&mut self, sink: &mut Vec<Pair>, nodes: &[HtmlNode]) {
|
||||
for node in nodes {
|
||||
match node {
|
||||
HtmlNode::Tag(tag) => self.discover_in_tag(
|
||||
sink,
|
||||
tag,
|
||||
Position { page: NonZeroUsize::ONE, point: Point::zero() },
|
||||
),
|
||||
HtmlNode::Text(_, _) => {}
|
||||
HtmlNode::Element(elem) => self.discover_in_html(sink, elem),
|
||||
HtmlNode::Element(elem) => self.discover_in_html(sink, &elem.children),
|
||||
HtmlNode::Frame(frame) => self.discover_in_frame(
|
||||
sink,
|
||||
frame,
|
||||
|
@ -1526,11 +1526,7 @@ impl<'a> CellGrid<'a> {
|
||||
self.entry(x, y).map(|entry| match entry {
|
||||
Entry::Cell(_) => Axes::new(x, y),
|
||||
Entry::Merged { parent } => {
|
||||
let c = if self.has_gutter {
|
||||
1 + self.cols.len() / 2
|
||||
} else {
|
||||
self.cols.len()
|
||||
};
|
||||
let c = self.non_gutter_column_count();
|
||||
let factor = if self.has_gutter { 2 } else { 1 };
|
||||
Axes::new(factor * (*parent % c), factor * (*parent / c))
|
||||
}
|
||||
@ -1602,6 +1598,21 @@ impl<'a> CellGrid<'a> {
|
||||
cell.rowspan.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn non_gutter_column_count(&self) -> usize {
|
||||
if self.has_gutter {
|
||||
// Calculation: With gutters, we have
|
||||
// 'cols = 2 * (non-gutter cols) - 1', since there is a gutter
|
||||
// column between each regular column. Therefore,
|
||||
// 'floor(cols / 2)' will be equal to
|
||||
// 'floor(non-gutter cols - 1/2) = non-gutter-cols - 1',
|
||||
// so 'non-gutter cols = 1 + floor(cols / 2)'.
|
||||
1 + self.cols.len() / 2
|
||||
} else {
|
||||
self.cols.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a cell's requested x and y, the vector with the resolved cell
|
||||
|
@ -282,7 +282,7 @@ fn show_cell_html(tag: HtmlTag, cell: &Cell, styles: StyleChain) -> Content {
|
||||
|
||||
fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
|
||||
let elem = |tag, body| HtmlElem::new(tag).with_body(Some(body)).pack();
|
||||
let mut rows: Vec<_> = grid.entries.chunks(grid.cols.len()).collect();
|
||||
let mut rows: Vec<_> = grid.entries.chunks(grid.non_gutter_column_count()).collect();
|
||||
|
||||
let tr = |tag, row: &[Entry]| {
|
||||
let row = row
|
||||
|
26
tests/ref/html/col-gutter-table.html
Normal file
26
tests/ref/html/col-gutter-table.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>h</td>
|
||||
<td>i</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
26
tests/ref/html/col-row-gutter-table.html
Normal file
26
tests/ref/html/col-row-gutter-table.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>h</td>
|
||||
<td>i</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
2
tests/ref/html/html-elem-alone-context.html
Normal file
2
tests/ref/html/html-elem-alone-context.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE html>
|
||||
<html></html>
|
2
tests/ref/html/html-elem-metadata.html
Normal file
2
tests/ref/html/html-elem-metadata.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE html>
|
||||
<html>Hi</html>
|
26
tests/ref/html/row-gutter-table.html
Normal file
26
tests/ref/html/row-gutter-table.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>h</td>
|
||||
<td>i</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@ -30,6 +30,8 @@
|
||||
// Hint: 7-26 compare with the literal type instead
|
||||
// Hint: 7-26 this comparison will always return `false` in future Typst releases
|
||||
#test(type(10) != "float", true)
|
||||
// This is not a warning.
|
||||
#test(type(10) in ("any", str, int), true)
|
||||
|
||||
--- type-string-compatibility-in-array ---
|
||||
// Warning: 7-35 comparing strings with types is deprecated
|
||||
|
15
tests/suite/html/elem.typ
Normal file
15
tests/suite/html/elem.typ
Normal file
@ -0,0 +1,15 @@
|
||||
--- html-elem-alone-context html ---
|
||||
#context html.elem("html")
|
||||
|
||||
--- html-elem-not-alone html ---
|
||||
// Error: 2-19 `<html>` element must be the only element in the document
|
||||
#html.elem("html")
|
||||
Text
|
||||
|
||||
--- html-elem-metadata html ---
|
||||
#html.elem("html", context {
|
||||
let val = query(<l>).first().value
|
||||
test(val, "Hi")
|
||||
val
|
||||
})
|
||||
#metadata("Hi") <l>
|
@ -30,3 +30,30 @@
|
||||
[row],
|
||||
),
|
||||
)
|
||||
|
||||
--- col-gutter-table html ---
|
||||
#table(
|
||||
columns: 3,
|
||||
column-gutter: 3pt,
|
||||
[a], [b], [c],
|
||||
[d], [e], [f],
|
||||
[g], [h], [i]
|
||||
)
|
||||
|
||||
--- row-gutter-table html ---
|
||||
#table(
|
||||
columns: 3,
|
||||
row-gutter: 3pt,
|
||||
[a], [b], [c],
|
||||
[d], [e], [f],
|
||||
[g], [h], [i]
|
||||
)
|
||||
|
||||
--- col-row-gutter-table html ---
|
||||
#table(
|
||||
columns: 3,
|
||||
gutter: 3pt,
|
||||
[a], [b], [c],
|
||||
[d], [e], [f],
|
||||
[g], [h], [i]
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user