mirror of
https://github.com/typst/typst
synced 2025-08-28 05:34:13 +08:00
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
04fd0acaca
commit
5dd5771df0
@ -205,7 +205,9 @@ impl Eval for ast::Label<'_> {
|
|||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Value::Label(Label::new(PicoStr::intern(self.get()))))
|
Ok(Value::Label(
|
||||||
|
Label::new(PicoStr::intern(self.get())).expect("unexpected empty label"),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +215,8 @@ impl Eval for ast::Ref<'_> {
|
|||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let target = Label::new(PicoStr::intern(self.target()));
|
let target = Label::new(PicoStr::intern(self.target()))
|
||||||
|
.expect("unexpected empty reference");
|
||||||
let mut elem = RefElem::new(target);
|
let mut elem = RefElem::new(target);
|
||||||
if let Some(supplement) = self.supplement() {
|
if let Some(supplement) = self.supplement() {
|
||||||
elem.push_supplement(Smart::Custom(Some(Supplement::Content(
|
elem.push_supplement(Smart::Custom(Some(Supplement::Content(
|
||||||
|
@ -72,7 +72,8 @@ pub fn definition(
|
|||||||
|
|
||||||
// Try to jump to the referenced content.
|
// Try to jump to the referenced content.
|
||||||
DerefTarget::Ref(node) => {
|
DerefTarget::Ref(node) => {
|
||||||
let label = Label::new(PicoStr::intern(node.cast::<ast::Ref>()?.target()));
|
let label = Label::new(PicoStr::intern(node.cast::<ast::Ref>()?.target()))
|
||||||
|
.expect("unexpected empty reference");
|
||||||
let selector = Selector::Label(label);
|
let selector = Selector::Label(label);
|
||||||
let elem = document?.introspector.query_first(&selector)?;
|
let elem = document?.introspector.query_first(&selector)?;
|
||||||
return Some(Definition::Span(elem.span()));
|
return Some(Definition::Span(elem.span()));
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
use typst_utils::{PicoStr, ResolvedPicoStr};
|
use typst_utils::{PicoStr, ResolvedPicoStr};
|
||||||
|
|
||||||
use crate::foundations::{func, scope, ty, Repr, Str};
|
use crate::diag::StrResult;
|
||||||
|
use crate::foundations::{bail, func, scope, ty, Repr, Str};
|
||||||
|
|
||||||
/// A label for an element.
|
/// A label for an element.
|
||||||
///
|
///
|
||||||
@ -27,7 +28,8 @@ use crate::foundations::{func, scope, ty, Repr, Str};
|
|||||||
/// # Syntax
|
/// # Syntax
|
||||||
/// This function also has dedicated syntax: You can create a label by enclosing
|
/// This function also has dedicated syntax: You can create a label by enclosing
|
||||||
/// its name in angle brackets. This works both in markup and code. A label's
|
/// its name in angle brackets. This works both in markup and code. A label's
|
||||||
/// name can contain letters, numbers, `_`, `-`, `:`, and `.`.
|
/// name can contain letters, numbers, `_`, `-`, `:`, and `.`. A label cannot
|
||||||
|
/// be empty.
|
||||||
///
|
///
|
||||||
/// Note that there is a syntactical difference when using the dedicated syntax
|
/// Note that there is a syntactical difference when using the dedicated syntax
|
||||||
/// for this function. In the code below, the `[<a>]` terminates the heading and
|
/// for this function. In the code below, the `[<a>]` terminates the heading and
|
||||||
@ -50,8 +52,11 @@ pub struct Label(PicoStr);
|
|||||||
|
|
||||||
impl Label {
|
impl Label {
|
||||||
/// Creates a label from an interned string.
|
/// Creates a label from an interned string.
|
||||||
pub fn new(name: PicoStr) -> Self {
|
///
|
||||||
Self(name)
|
/// Returns `None` if the given string is empty.
|
||||||
|
pub fn new(name: PicoStr) -> Option<Self> {
|
||||||
|
const EMPTY: PicoStr = PicoStr::constant("");
|
||||||
|
(name != EMPTY).then_some(Self(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the label to a string.
|
/// Resolves the label to a string.
|
||||||
@ -70,10 +75,14 @@ impl Label {
|
|||||||
/// Creates a label from a string.
|
/// Creates a label from a string.
|
||||||
#[func(constructor)]
|
#[func(constructor)]
|
||||||
pub fn construct(
|
pub fn construct(
|
||||||
/// The name of the label.
|
/// The name of the label. Must not be empty.
|
||||||
name: Str,
|
name: Str,
|
||||||
) -> Label {
|
) -> StrResult<Label> {
|
||||||
Self(PicoStr::intern(name.as_str()))
|
if name.is_empty() {
|
||||||
|
bail!("label name must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self(PicoStr::intern(name.as_str())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +321,11 @@ impl Bibliography {
|
|||||||
for d in data.iter() {
|
for d in data.iter() {
|
||||||
let library = decode_library(d)?;
|
let library = decode_library(d)?;
|
||||||
for entry in library {
|
for entry in library {
|
||||||
match map.entry(Label::new(PicoStr::intern(entry.key()))) {
|
let label = Label::new(PicoStr::intern(entry.key()))
|
||||||
|
.ok_or("bibliography contains entry with empty key")
|
||||||
|
.at(d.source.span)?;
|
||||||
|
|
||||||
|
match map.entry(label) {
|
||||||
indexmap::map::Entry::Vacant(vacant) => {
|
indexmap::map::Entry::Vacant(vacant) => {
|
||||||
vacant.insert(entry);
|
vacant.insert(entry);
|
||||||
}
|
}
|
||||||
|
@ -724,6 +724,8 @@ node! {
|
|||||||
|
|
||||||
impl<'a> Ref<'a> {
|
impl<'a> Ref<'a> {
|
||||||
/// Get the target.
|
/// Get the target.
|
||||||
|
///
|
||||||
|
/// Will not be empty.
|
||||||
pub fn target(self) -> &'a str {
|
pub fn target(self) -> &'a str {
|
||||||
self.0
|
self.0
|
||||||
.children()
|
.children()
|
||||||
|
@ -185,7 +185,7 @@ impl Lexer<'_> {
|
|||||||
'h' if self.s.eat_if("ttp://") => self.link(),
|
'h' if self.s.eat_if("ttp://") => self.link(),
|
||||||
'h' if self.s.eat_if("ttps://") => self.link(),
|
'h' if self.s.eat_if("ttps://") => self.link(),
|
||||||
'<' if self.s.at(is_id_continue) => self.label(),
|
'<' if self.s.at(is_id_continue) => self.label(),
|
||||||
'@' => self.ref_marker(),
|
'@' if self.s.at(is_id_continue) => self.ref_marker(),
|
||||||
|
|
||||||
'.' if self.s.eat_if("..") => SyntaxKind::Shorthand,
|
'.' if self.s.eat_if("..") => SyntaxKind::Shorthand,
|
||||||
'-' if self.s.eat_if("--") => SyntaxKind::Shorthand,
|
'-' if self.s.eat_if("--") => SyntaxKind::Shorthand,
|
||||||
|
BIN
tests/ref/ref-to-empty-label-not-possible.png
Normal file
BIN
tests/ref/ref-to-empty-label-not-possible.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 B |
@ -92,3 +92,7 @@ _Visible_
|
|||||||
--- label-non-existent-error ---
|
--- label-non-existent-error ---
|
||||||
// Error: 5-10 sequence does not have field "label"
|
// Error: 5-10 sequence does not have field "label"
|
||||||
#[].label
|
#[].label
|
||||||
|
|
||||||
|
--- label-empty ---
|
||||||
|
// Error: 23-32 label name must not be empty
|
||||||
|
= Something to label #label("")
|
||||||
|
@ -75,6 +75,14 @@ Now we have multiple bibliographies containing @glacier-melt @keshav2007read
|
|||||||
// Error: 2-62 CSL style "Alphanumeric" is not suitable for bibliographies
|
// Error: 2-62 CSL style "Alphanumeric" is not suitable for bibliographies
|
||||||
#bibliography("/assets/bib/works.bib", style: "alphanumeric")
|
#bibliography("/assets/bib/works.bib", style: "alphanumeric")
|
||||||
|
|
||||||
|
--- bibliography-empty-key ---
|
||||||
|
#let src = ```yaml
|
||||||
|
"":
|
||||||
|
type: Book
|
||||||
|
```
|
||||||
|
// Error: 15-30 bibliography contains entry with empty key
|
||||||
|
#bibliography(bytes(src.text))
|
||||||
|
|
||||||
--- issue-4618-bibliography-set-heading-level ---
|
--- issue-4618-bibliography-set-heading-level ---
|
||||||
// Test that the bibliography block's heading is set to 2 by the show rule,
|
// Test that the bibliography block's heading is set to 2 by the show rule,
|
||||||
// and therefore should be rendered like a level-2 heading. Notably, this
|
// and therefore should be rendered like a level-2 heading. Notably, this
|
||||||
|
@ -86,3 +86,14 @@ Text seen on #ref(<text>, form: "page", supplement: "Page").
|
|||||||
// Test reference with non-whitespace before it.
|
// Test reference with non-whitespace before it.
|
||||||
#figure[] <1>
|
#figure[] <1>
|
||||||
#test([(#ref(<1>))], [(@1)])
|
#test([(#ref(<1>))], [(@1)])
|
||||||
|
|
||||||
|
--- ref-to-empty-label-not-possible ---
|
||||||
|
// @ without any following label should just produce the symbol in the output
|
||||||
|
// and not produce a reference to a label with an empty name.
|
||||||
|
@
|
||||||
|
|
||||||
|
--- ref-function-empty-label ---
|
||||||
|
// using ref() should also not be possible
|
||||||
|
// Error: 6-7 unexpected less-than operator
|
||||||
|
// Error: 7-8 unexpected greater-than operator
|
||||||
|
#ref(<>)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user