Compare commits

..

5 Commits

Author SHA1 Message Date
Tobias Schmitz
d99ebe42ac
feat: [WIP] allow specifying alt text for links
skip-checks:true
2025-05-28 17:47:35 +02:00
Tobias Schmitz
0d2c43c4be
feat: [WIP] include links in tag tree
skip-checks:true
2025-05-28 15:10:00 +02:00
Tobias Schmitz
19d54b6310
feat: [WIP] write tags
skip-checks:true
2025-05-28 15:10:00 +02:00
Tobias Schmitz
f185ff4953
feat: [WIP] make more things locatable
skip-checks:true
2025-05-28 15:10:00 +02:00
Tobias Schmitz
ec53eb3da2
feat: [draft] generate accessibility tag tree for headings
skip-checks:true
2025-05-28 15:09:55 +02:00
13 changed files with 33 additions and 115 deletions

View File

@ -37,12 +37,7 @@ impl Eval for ast::FuncCall<'_> {
let target = access.target();
let field = access.field();
match eval_field_call(target, field, args, span, vm)? {
FieldCall::Normal(callee, args) => {
if vm.inspected == Some(callee_span) {
vm.trace(callee.clone());
}
(callee, args)
}
FieldCall::Normal(callee, args) => (callee, args),
FieldCall::Resolved(value) => return Ok(value),
}
} else {

View File

@ -15,7 +15,7 @@ use typst::syntax::{
ast, is_id_continue, is_id_start, is_ident, FileId, LinkedNode, Side, Source,
SyntaxKind,
};
use typst::text::{FontFlags, RawElem};
use typst::text::RawElem;
use typst::visualize::Color;
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.
struct CompletionContext<'a> {
world: &'a (dyn IdeWorld + 'a),
@ -1170,12 +1152,10 @@ impl<'a> CompletionContext<'a> {
/// Add completions for all font families.
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() {
let variants: Vec<_> = iter.collect();
let is_math = variants.iter().any(|f| f.flags.contains(FontFlags::MATH));
let detail = summarize_font_family(variants);
if !equation || is_math {
let detail = summarize_font_family(iter);
if !equation || family.contains("Math") {
self.str_completion(
family,
Some(CompletionKind::Font),
@ -1810,21 +1790,4 @@ mod tests {
.must_include(["r", "dashed"])
.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\""]);
}
}

View File

@ -269,7 +269,7 @@ fn font_tooltip(world: &dyn IdeWorld, leaf: &LinkedNode) -> Option<Tooltip> {
.find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str());
then {
let detail = summarize_font_family(iter.collect());
let detail = summarize_font_family(iter);
return Some(Tooltip::Text(detail));
}
};
@ -371,11 +371,4 @@ mod tests {
test(&world, -2, Side::Before).must_be_none();
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("(..) => ..");
}
}

View File

@ -77,20 +77,23 @@ pub fn plain_docs_sentence(docs: &str) -> EcoString {
}
/// Create a short description of a font family.
pub fn summarize_font_family(mut variants: Vec<&FontInfo>) -> EcoString {
variants.sort_by_key(|info| info.variant);
pub fn summarize_font_family<'a>(
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 min_weight = u16::MAX;
let mut max_weight = 0;
for info in &variants {
for info in &infos {
let weight = info.variant.weight.to_number();
has_italic |= info.variant.style == FontStyle::Italic;
min_weight = min_weight.min(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" });
if min_weight == max_weight {

View File

@ -11,7 +11,7 @@ use typst_library::layout::{
};
use typst_library::math::{EquationElem, MathSize};
use typst_library::text::{Font, Glyph, Lang, Region, TextElem, TextItem};
use typst_library::visualize::{FixedStroke, Paint};
use typst_library::visualize::Paint;
use typst_syntax::Span;
use typst_utils::default_math_class;
use unicode_math_class::MathClass;
@ -235,7 +235,6 @@ pub struct GlyphFragment {
pub lang: Lang,
pub region: Option<Region>,
pub fill: Paint,
pub stroke: Option<FixedStroke>,
pub shift: Abs,
pub width: Abs,
pub ascent: Abs,
@ -287,7 +286,6 @@ impl GlyphFragment {
lang: TextElem::lang_in(styles),
region: TextElem::region_in(styles),
fill: TextElem::fill_in(styles).as_decoration(),
stroke: TextElem::stroke_in(styles).map(|s| s.unwrap_or_default()),
shift: TextElem::baseline_in(styles),
font_size: TextElem::size_in(styles),
math_size: EquationElem::size_in(styles),
@ -370,10 +368,10 @@ impl GlyphFragment {
font: self.font.clone(),
size: self.font_size,
fill: self.fill,
stroke: self.stroke,
lang: self.lang,
region: self.region,
text: self.c.into(),
stroke: None,
glyphs: vec![Glyph {
id: self.id.0,
x_advance: Em::from_length(self.width, self.font_size),

View File

@ -278,9 +278,6 @@ impl MathRun {
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> {
let mut items = vec![];
@ -298,25 +295,22 @@ impl MathRun {
let mut space_is_visible = false;
let is_relation = |f: &MathFragment| matches!(f.class(), MathClass::Relation);
let is_space = |f: &MathFragment| {
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();
while let Some(fragment) = iter.next() {
if space_is_visible && is_space(&fragment) {
items.push(InlineItem::Space(fragment.width(), true));
if space_is_visible {
match fragment {
MathFragment::Space(width) | MathFragment::Spacing(width, _) => {
items.push(InlineItem::Space(width, true));
continue;
}
_ => {}
}
}
let class = fragment.class();
let y = fragment.ascent();
@ -329,9 +323,10 @@ impl MathRun {
frame.push_frame(pos, fragment.into_frame());
empty = false;
// Split our current frame when we encounter a binary operator or
// relation so that there is a line-breaking opportunity.
if is_line_break_opportunity(class, iter.peek().map(|f| f.class())) {
if class == MathClass::Binary
|| (class == MathClass::Relation
&& !iter.peek().map(is_relation).unwrap_or_default())
{
let mut frame_prev =
std::mem::replace(&mut frame, Frame::soft(Size::zero()));

View File

@ -41,23 +41,8 @@ use crate::layout::{BlockElem, Size};
/// receives the page's dimensions minus its margins. This is mostly useful in
/// combination with [measurement]($measure).
///
/// To retrieve the _remaining_ size of the page rather than its full size, you
/// you can wrap your `layout` call in a `{block(height: 1fr)}`. This works
/// 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.
/// You can also use this function to resolve [`ratio`] to fixed lengths. This
/// might come in handy if you're building your own layout abstractions.
///
/// ```example
/// #layout(size => {

View File

@ -21,10 +21,9 @@ use crate::text::TextElem;
///
/// The default, a `{"normal"}` reference, produces a textual reference to a
/// 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
/// "Section" depends on the [`lang`]($text.lang) setting and is localized
/// accordingly. The references are also links to the respective element.
/// Reference syntax can also be used to [cite] from a bibliography.
/// string such as "Section 1" for a reference to the first heading. The
/// references are also links to the respective element. Reference syntax can
/// also be used to [cite] from a bibliography.
///
/// As the default form requires a supplement and numbering, the label must be
/// attached to a _referenceable element_. Referenceable elements include

View File

@ -194,8 +194,6 @@ bitflags::bitflags! {
const MONOSPACE = 1 << 0;
/// Glyphs have short strokes at their stems.
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();
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.
if let Some(panose) = ttf

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 439 B

View File

@ -297,10 +297,3 @@ Looks at the @quadratic formula.
#set page(width: 150pt)
#set text(lang: "he")
תהא סדרה $a_n$: $[a_n: 1, 1/2, 1/3, dots]$
--- issue-6170-equation-stroke ---
// In this bug stroke settings did not apply to math content.
// We expect all of these to have a green stroke.
#set text(stroke: green + 0.5pt)
A $B^2$ $ grave(C)' $

View File

@ -99,9 +99,6 @@ Multiple trailing line breaks.
#let hrule(x) = box(line(length: x))
#hrule(90pt)$<;$\
#hrule(95pt)$<;$\
// We don't linebreak before a closing paren, but do before an opening paren.
#hrule(90pt)$<($\
#hrule(95pt)$<($
#hrule(90pt)$<)$\
#hrule(95pt)$<)$