mirror of
https://github.com/typst/typst
synced 2025-08-20 09:49:02 +08:00
Compare commits
No commits in common. "82e869023c7a7f31d716e7706a9a176b3d909279" and "2a258a0c3849073c56a0559dbd67ee2effcd1031" have entirely different histories.
82e869023c
...
2a258a0c38
@ -37,12 +37,7 @@ impl Eval for ast::FuncCall<'_> {
|
|||||||
let target = access.target();
|
let target = access.target();
|
||||||
let field = access.field();
|
let field = access.field();
|
||||||
match eval_field_call(target, field, args, span, vm)? {
|
match eval_field_call(target, field, args, span, vm)? {
|
||||||
FieldCall::Normal(callee, args) => {
|
FieldCall::Normal(callee, args) => (callee, args),
|
||||||
if vm.inspected == Some(callee_span) {
|
|
||||||
vm.trace(callee.clone());
|
|
||||||
}
|
|
||||||
(callee, args)
|
|
||||||
}
|
|
||||||
FieldCall::Resolved(value) => return Ok(value),
|
FieldCall::Resolved(value) => return Ok(value),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,7 +15,7 @@ use typst::syntax::{
|
|||||||
ast, is_id_continue, is_id_start, is_ident, FileId, LinkedNode, Side, Source,
|
ast, is_id_continue, is_id_start, is_ident, FileId, LinkedNode, Side, Source,
|
||||||
SyntaxKind,
|
SyntaxKind,
|
||||||
};
|
};
|
||||||
use typst::text::{FontFlags, RawElem};
|
use typst::text::RawElem;
|
||||||
use typst::visualize::Color;
|
use typst::visualize::Color;
|
||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
@ -1081,24 +1081,6 @@ fn code_completions(ctx: &mut CompletionContext, hash: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See if the AST node is somewhere within a show rule applying to equations
|
|
||||||
fn is_in_equation_show_rule(leaf: &LinkedNode<'_>) -> bool {
|
|
||||||
let mut node = leaf;
|
|
||||||
while let Some(parent) = node.parent() {
|
|
||||||
if_chain! {
|
|
||||||
if let Some(expr) = parent.get().cast::<ast::Expr>();
|
|
||||||
if let ast::Expr::ShowRule(show) = expr;
|
|
||||||
if let Some(ast::Expr::FieldAccess(field)) = show.selector();
|
|
||||||
if field.field().as_str() == "equation";
|
|
||||||
then {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node = parent;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Context for autocompletion.
|
/// Context for autocompletion.
|
||||||
struct CompletionContext<'a> {
|
struct CompletionContext<'a> {
|
||||||
world: &'a (dyn IdeWorld + 'a),
|
world: &'a (dyn IdeWorld + 'a),
|
||||||
@ -1170,12 +1152,10 @@ impl<'a> CompletionContext<'a> {
|
|||||||
|
|
||||||
/// Add completions for all font families.
|
/// Add completions for all font families.
|
||||||
fn font_completions(&mut self) {
|
fn font_completions(&mut self) {
|
||||||
let equation = is_in_equation_show_rule(self.leaf);
|
let equation = self.before_window(25).contains("equation");
|
||||||
for (family, iter) in self.world.book().families() {
|
for (family, iter) in self.world.book().families() {
|
||||||
let variants: Vec<_> = iter.collect();
|
let detail = summarize_font_family(iter);
|
||||||
let is_math = variants.iter().any(|f| f.flags.contains(FontFlags::MATH));
|
if !equation || family.contains("Math") {
|
||||||
let detail = summarize_font_family(variants);
|
|
||||||
if !equation || is_math {
|
|
||||||
self.str_completion(
|
self.str_completion(
|
||||||
family,
|
family,
|
||||||
Some(CompletionKind::Font),
|
Some(CompletionKind::Font),
|
||||||
@ -1810,21 +1790,4 @@ mod tests {
|
|||||||
.must_include(["r", "dashed"])
|
.must_include(["r", "dashed"])
|
||||||
.must_exclude(["cases"]);
|
.must_exclude(["cases"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_autocomplete_fonts() {
|
|
||||||
test("#text(font:)", -1)
|
|
||||||
.must_include(["\"Libertinus Serif\"", "\"New Computer Modern Math\""]);
|
|
||||||
|
|
||||||
test("#show link: set text(font: )", -1)
|
|
||||||
.must_include(["\"Libertinus Serif\"", "\"New Computer Modern Math\""]);
|
|
||||||
|
|
||||||
test("#show math.equation: set text(font: )", -1)
|
|
||||||
.must_include(["\"New Computer Modern Math\""])
|
|
||||||
.must_exclude(["\"Libertinus Serif\""]);
|
|
||||||
|
|
||||||
test("#show math.equation: it => { set text(font: )\nit }", -6)
|
|
||||||
.must_include(["\"New Computer Modern Math\""])
|
|
||||||
.must_exclude(["\"Libertinus Serif\""]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@ fn font_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
|
|||||||
.find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str());
|
.find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str());
|
||||||
|
|
||||||
then {
|
then {
|
||||||
let detail = summarize_font_family(iter.collect());
|
let detail = summarize_font_family(iter);
|
||||||
return Some(Tooltip::Text(detail));
|
return Some(Tooltip::Text(detail));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -371,11 +371,4 @@ mod tests {
|
|||||||
test(&world, -2, Side::Before).must_be_none();
|
test(&world, -2, Side::Before).must_be_none();
|
||||||
test(&world, -2, Side::After).must_be_text("This star imports `a`, `b`, and `c`");
|
test(&world, -2, Side::After).must_be_text("This star imports `a`, `b`, and `c`");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_tooltip_field_call() {
|
|
||||||
let world = TestWorld::new("#import \"other.typ\"\n#other.f()")
|
|
||||||
.with_source("other.typ", "#let f = (x) => 1");
|
|
||||||
test(&world, -4, Side::After).must_be_code("(..) => ..");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -77,20 +77,23 @@ pub fn plain_docs_sentence(docs: &str) -> EcoString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a short description of a font family.
|
/// Create a short description of a font family.
|
||||||
pub fn summarize_font_family(mut variants: Vec<&FontInfo>) -> EcoString {
|
pub fn summarize_font_family<'a>(
|
||||||
variants.sort_by_key(|info| info.variant);
|
variants: impl Iterator<Item = &'a FontInfo>,
|
||||||
|
) -> EcoString {
|
||||||
|
let mut infos: Vec<_> = variants.collect();
|
||||||
|
infos.sort_by_key(|info| info.variant);
|
||||||
|
|
||||||
let mut has_italic = false;
|
let mut has_italic = false;
|
||||||
let mut min_weight = u16::MAX;
|
let mut min_weight = u16::MAX;
|
||||||
let mut max_weight = 0;
|
let mut max_weight = 0;
|
||||||
for info in &variants {
|
for info in &infos {
|
||||||
let weight = info.variant.weight.to_number();
|
let weight = info.variant.weight.to_number();
|
||||||
has_italic |= info.variant.style == FontStyle::Italic;
|
has_italic |= info.variant.style == FontStyle::Italic;
|
||||||
min_weight = min_weight.min(weight);
|
min_weight = min_weight.min(weight);
|
||||||
max_weight = min_weight.max(weight);
|
max_weight = min_weight.max(weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = variants.len();
|
let count = infos.len();
|
||||||
let mut detail = eco_format!("{count} variant{}.", if count == 1 { "" } else { "s" });
|
let mut detail = eco_format!("{count} variant{}.", if count == 1 { "" } else { "s" });
|
||||||
|
|
||||||
if min_weight == max_weight {
|
if min_weight == max_weight {
|
||||||
|
@ -278,9 +278,6 @@ impl MathRun {
|
|||||||
frame
|
frame
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this run of math fragments into a vector of inline items for
|
|
||||||
/// paragraph layout. Creates multiple fragments when relation or binary
|
|
||||||
/// operators are present to allow for line-breaking opportunities later.
|
|
||||||
pub fn into_par_items(self) -> Vec<InlineItem> {
|
pub fn into_par_items(self) -> Vec<InlineItem> {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
@ -298,24 +295,21 @@ impl MathRun {
|
|||||||
|
|
||||||
let mut space_is_visible = false;
|
let mut space_is_visible = false;
|
||||||
|
|
||||||
|
let is_relation = |f: &MathFragment| matches!(f.class(), MathClass::Relation);
|
||||||
let is_space = |f: &MathFragment| {
|
let is_space = |f: &MathFragment| {
|
||||||
matches!(f, MathFragment::Space(_) | MathFragment::Spacing(_, _))
|
matches!(f, MathFragment::Space(_) | MathFragment::Spacing(_, _))
|
||||||
};
|
};
|
||||||
let is_line_break_opportunity = |class, next_fragment| match class {
|
|
||||||
// Don't split when two relations are in a row or when preceding a
|
|
||||||
// closing parenthesis.
|
|
||||||
MathClass::Binary => next_fragment != Some(MathClass::Closing),
|
|
||||||
MathClass::Relation => {
|
|
||||||
!matches!(next_fragment, Some(MathClass::Relation | MathClass::Closing))
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut iter = self.0.into_iter().peekable();
|
let mut iter = self.0.into_iter().peekable();
|
||||||
while let Some(fragment) = iter.next() {
|
while let Some(fragment) = iter.next() {
|
||||||
if space_is_visible && is_space(&fragment) {
|
if space_is_visible {
|
||||||
items.push(InlineItem::Space(fragment.width(), true));
|
match fragment {
|
||||||
continue;
|
MathFragment::Space(width) | MathFragment::Spacing(width, _) => {
|
||||||
|
items.push(InlineItem::Space(width, true));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let class = fragment.class();
|
let class = fragment.class();
|
||||||
@ -329,9 +323,10 @@ impl MathRun {
|
|||||||
frame.push_frame(pos, fragment.into_frame());
|
frame.push_frame(pos, fragment.into_frame());
|
||||||
empty = false;
|
empty = false;
|
||||||
|
|
||||||
// Split our current frame when we encounter a binary operator or
|
if class == MathClass::Binary
|
||||||
// relation so that there is a line-breaking opportunity.
|
|| (class == MathClass::Relation
|
||||||
if is_line_break_opportunity(class, iter.peek().map(|f| f.class())) {
|
&& !iter.peek().map(is_relation).unwrap_or_default())
|
||||||
|
{
|
||||||
let mut frame_prev =
|
let mut frame_prev =
|
||||||
std::mem::replace(&mut frame, Frame::soft(Size::zero()));
|
std::mem::replace(&mut frame, Frame::soft(Size::zero()));
|
||||||
|
|
||||||
|
@ -41,23 +41,8 @@ use crate::layout::{BlockElem, Size};
|
|||||||
/// receives the page's dimensions minus its margins. This is mostly useful in
|
/// receives the page's dimensions minus its margins. This is mostly useful in
|
||||||
/// combination with [measurement]($measure).
|
/// combination with [measurement]($measure).
|
||||||
///
|
///
|
||||||
/// To retrieve the _remaining_ size of the page rather than its full size, you
|
/// You can also use this function to resolve [`ratio`] to fixed lengths. This
|
||||||
/// you can wrap your `layout` call in a `{block(height: 1fr)}`. This works
|
/// might come in handy if you're building your own layout abstractions.
|
||||||
/// because the block automatically grows to fill the remaining space (see the
|
|
||||||
/// [fraction] documentation for more details).
|
|
||||||
///
|
|
||||||
/// ```example
|
|
||||||
/// #set page(height: 150pt)
|
|
||||||
///
|
|
||||||
/// #lorem(20)
|
|
||||||
///
|
|
||||||
/// #block(height: 1fr, layout(size => [
|
|
||||||
/// Remaining height: #size.height
|
|
||||||
/// ]))
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// You can also use this function to resolve a [`ratio`] to a fixed length.
|
|
||||||
/// This might come in handy if you're building your own layout abstractions.
|
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #layout(size => {
|
/// #layout(size => {
|
||||||
|
@ -21,10 +21,9 @@ use crate::text::TextElem;
|
|||||||
///
|
///
|
||||||
/// The default, a `{"normal"}` reference, produces a textual reference to a
|
/// The default, a `{"normal"}` reference, produces a textual reference to a
|
||||||
/// label. For example, a reference to a heading will yield an appropriate
|
/// label. For example, a reference to a heading will yield an appropriate
|
||||||
/// string such as "Section 1" for a reference to the first heading. The word
|
/// string such as "Section 1" for a reference to the first heading. The
|
||||||
/// "Section" depends on the [`lang`]($text.lang) setting and is localized
|
/// references are also links to the respective element. Reference syntax can
|
||||||
/// accordingly. The references are also links to the respective element.
|
/// also be used to [cite] from a bibliography.
|
||||||
/// Reference syntax can also be used to [cite] from a bibliography.
|
|
||||||
///
|
///
|
||||||
/// As the default form requires a supplement and numbering, the label must be
|
/// As the default form requires a supplement and numbering, the label must be
|
||||||
/// attached to a _referenceable element_. Referenceable elements include
|
/// attached to a _referenceable element_. Referenceable elements include
|
||||||
|
@ -194,8 +194,6 @@ bitflags::bitflags! {
|
|||||||
const MONOSPACE = 1 << 0;
|
const MONOSPACE = 1 << 0;
|
||||||
/// Glyphs have short strokes at their stems.
|
/// Glyphs have short strokes at their stems.
|
||||||
const SERIF = 1 << 1;
|
const SERIF = 1 << 1;
|
||||||
/// Font face has a MATH table
|
|
||||||
const MATH = 1 << 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +272,6 @@ impl FontInfo {
|
|||||||
|
|
||||||
let mut flags = FontFlags::empty();
|
let mut flags = FontFlags::empty();
|
||||||
flags.set(FontFlags::MONOSPACE, ttf.is_monospaced());
|
flags.set(FontFlags::MONOSPACE, ttf.is_monospaced());
|
||||||
flags.set(FontFlags::MATH, ttf.tables().math.is_some());
|
|
||||||
|
|
||||||
// Determine whether this is a serif or sans-serif font.
|
// Determine whether this is a serif or sans-serif font.
|
||||||
if let Some(panose) = ttf
|
if let Some(panose) = ttf
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 439 B |
@ -99,9 +99,6 @@ Multiple trailing line breaks.
|
|||||||
#let hrule(x) = box(line(length: x))
|
#let hrule(x) = box(line(length: x))
|
||||||
#hrule(90pt)$<;$\
|
#hrule(90pt)$<;$\
|
||||||
#hrule(95pt)$<;$\
|
#hrule(95pt)$<;$\
|
||||||
// We don't linebreak before a closing paren, but do before an opening paren.
|
|
||||||
#hrule(90pt)$<($\
|
|
||||||
#hrule(95pt)$<($
|
|
||||||
#hrule(90pt)$<)$\
|
#hrule(90pt)$<)$\
|
||||||
#hrule(95pt)$<)$
|
#hrule(95pt)$<)$
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user