mirror of
https://github.com/typst/typst
synced 2025-08-18 00:48:34 +08:00
Compare commits
4 Commits
a60991a93e
...
383b9189ba
Author | SHA1 | Date | |
---|---|---|---|
|
383b9189ba | ||
|
0264534928 | ||
|
d735e2ff70 | ||
|
c97ce11dbb |
@ -130,7 +130,14 @@ fn complete_markup(ctx: &mut CompletionContext) -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start of a reference: "@|" or "@he|".
|
// Start of a reference: "@|".
|
||||||
|
if ctx.leaf.kind() == SyntaxKind::Text && ctx.before.ends_with("@") {
|
||||||
|
ctx.from = ctx.cursor;
|
||||||
|
ctx.label_completions();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An existing reference: "@he|".
|
||||||
if ctx.leaf.kind() == SyntaxKind::RefMarker {
|
if ctx.leaf.kind() == SyntaxKind::RefMarker {
|
||||||
ctx.from = ctx.leaf.offset() + 1;
|
ctx.from = ctx.leaf.offset() + 1;
|
||||||
ctx.label_completions();
|
ctx.label_completions();
|
||||||
@ -1644,6 +1651,19 @@ mod tests {
|
|||||||
test_with_doc(world, pos, doc.as_ref())
|
test_with_doc(world, pos, doc.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn test_with_addition(
|
||||||
|
initial_text: &str,
|
||||||
|
addition: &str,
|
||||||
|
pos: impl FilePos,
|
||||||
|
) -> Response {
|
||||||
|
let mut world = TestWorld::new(initial_text);
|
||||||
|
let doc = typst::compile(&world).output.ok();
|
||||||
|
let end = world.main.text().len();
|
||||||
|
world.main.edit(end..end, addition);
|
||||||
|
test_with_doc(&world, pos, doc.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn test_with_doc(
|
fn test_with_doc(
|
||||||
world: impl WorldLike,
|
world: impl WorldLike,
|
||||||
@ -1709,15 +1729,24 @@ mod tests {
|
|||||||
.must_exclude(["bib"]);
|
.must_exclude(["bib"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_ref_function() {
|
||||||
|
test_with_addition("x<test>", " #ref(<)", -2).must_include(["test"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_ref_shorthand() {
|
||||||
|
test_with_addition("x<test>", " @", -1).must_include(["test"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_autocomplete_ref_shorthand_with_partial_identifier() {
|
||||||
|
test_with_addition("x<test>", " @te", -1).must_include(["test"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_autocomplete_ref_identical_labels_returns_single_completion() {
|
fn test_autocomplete_ref_identical_labels_returns_single_completion() {
|
||||||
let mut world = TestWorld::new("x<test> y<test>");
|
let result = test_with_addition("x<test> y<test>", " @t", -1);
|
||||||
let doc = typst::compile(&world).output.ok();
|
|
||||||
|
|
||||||
let end = world.main.text().len();
|
|
||||||
world.main.edit(end..end, " @t");
|
|
||||||
|
|
||||||
let result = test_with_doc(&world, -1, doc.as_ref());
|
|
||||||
let completions = result.completions();
|
let completions = result.completions();
|
||||||
let label_count =
|
let label_count =
|
||||||
completions.iter().filter(|c| c.kind == CompletionKind::Label).count();
|
completions.iter().filter(|c| c.kind == CompletionKind::Label).count();
|
||||||
|
@ -298,7 +298,12 @@ impl Func {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Non-generic implementation of `call`.
|
/// Non-generic implementation of `call`.
|
||||||
#[typst_macros::time(name = "func call", span = self.span())]
|
#[typst_macros::time(
|
||||||
|
name = "func call",
|
||||||
|
span = self.span(),
|
||||||
|
callsite = args.span,
|
||||||
|
func = self.name().unwrap_or("anonymous")
|
||||||
|
)]
|
||||||
fn call_impl(
|
fn call_impl(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
@ -8,13 +8,15 @@ use crate::util::{kw, parse_key_value, parse_string};
|
|||||||
/// Expand the `#[time(..)]` macro.
|
/// Expand the `#[time(..)]` macro.
|
||||||
pub fn time(stream: TokenStream, item: syn::ItemFn) -> Result<TokenStream> {
|
pub fn time(stream: TokenStream, item: syn::ItemFn) -> Result<TokenStream> {
|
||||||
let meta: Meta = syn::parse2(stream)?;
|
let meta: Meta = syn::parse2(stream)?;
|
||||||
Ok(create(meta, item))
|
create(meta, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `..` in `#[time(..)]`.
|
/// The `..` in `#[time(..)]`.
|
||||||
pub struct Meta {
|
pub struct Meta {
|
||||||
pub span: Option<syn::Expr>,
|
pub span: Option<syn::Expr>,
|
||||||
|
pub callsite: Option<syn::Expr>,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
pub func: Option<syn::Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Meta {
|
impl Parse for Meta {
|
||||||
@ -22,15 +24,37 @@ impl Parse for Meta {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: parse_string::<kw::name>(input)?,
|
name: parse_string::<kw::name>(input)?,
|
||||||
span: parse_key_value::<kw::span, syn::Expr>(input)?,
|
span: parse_key_value::<kw::span, syn::Expr>(input)?,
|
||||||
|
callsite: parse_key_value::<kw::callsite, syn::Expr>(input)?,
|
||||||
|
func: parse_key_value::<kw::func, syn::Expr>(input)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(meta: Meta, mut item: syn::ItemFn) -> TokenStream {
|
fn create(meta: Meta, mut item: syn::ItemFn) -> Result<TokenStream> {
|
||||||
let name = meta.name.unwrap_or_else(|| item.sig.ident.to_string());
|
let name = meta.name.unwrap_or_else(|| item.sig.ident.to_string());
|
||||||
let construct = match meta.span.as_ref() {
|
|
||||||
Some(span) => quote! { with_span(#name, Some(#span.into_raw())) },
|
let func = match meta.func {
|
||||||
None => quote! { new(#name) },
|
Some(func) => {
|
||||||
|
if meta.callsite.is_none() {
|
||||||
|
bail!(func, "the `func` argument can only be used with a callsite")
|
||||||
|
}
|
||||||
|
|
||||||
|
quote! { Some(#func.into()) }
|
||||||
|
}
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
|
||||||
|
let construct = match (meta.span.as_ref(), meta.callsite.as_ref()) {
|
||||||
|
(Some(span), Some(callsite)) => quote! {
|
||||||
|
with_callsite(#name, Some(#span.into_raw()), Some(#callsite.into_raw()), #func)
|
||||||
|
},
|
||||||
|
(Some(span), None) => quote! {
|
||||||
|
with_span(#name, Some(#span.into_raw()))
|
||||||
|
},
|
||||||
|
(None, Some(expr)) => {
|
||||||
|
bail!(expr, "cannot have a callsite span without a main span")
|
||||||
|
}
|
||||||
|
(None, None) => quote! { new(#name) },
|
||||||
};
|
};
|
||||||
|
|
||||||
item.block.stmts.insert(
|
item.block.stmts.insert(
|
||||||
@ -40,5 +64,5 @@ fn create(meta: Meta, mut item: syn::ItemFn) -> TokenStream {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
item.into_token_stream()
|
Ok(item.into_token_stream())
|
||||||
}
|
}
|
||||||
|
@ -254,6 +254,8 @@ impl Parse for BareType {
|
|||||||
pub mod kw {
|
pub mod kw {
|
||||||
syn::custom_keyword!(name);
|
syn::custom_keyword!(name);
|
||||||
syn::custom_keyword!(span);
|
syn::custom_keyword!(span);
|
||||||
|
syn::custom_keyword!(callsite);
|
||||||
|
syn::custom_keyword!(func);
|
||||||
syn::custom_keyword!(title);
|
syn::custom_keyword!(title);
|
||||||
syn::custom_keyword!(scope);
|
syn::custom_keyword!(scope);
|
||||||
syn::custom_keyword!(contextual);
|
syn::custom_keyword!(contextual);
|
||||||
|
@ -33,7 +33,7 @@ use typst_syntax::Span;
|
|||||||
use typst_utils::{SliceExt, SmallBitSet};
|
use typst_utils::{SliceExt, SmallBitSet};
|
||||||
|
|
||||||
/// Realize content into a flat list of well-known, styled items.
|
/// Realize content into a flat list of well-known, styled items.
|
||||||
#[typst_macros::time(name = "realize")]
|
#[typst_macros::time(name = "realize", span = content.span())]
|
||||||
pub fn realize<'a>(
|
pub fn realize<'a>(
|
||||||
kind: RealizationKind,
|
kind: RealizationKind,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
|
@ -93,20 +93,26 @@ pub fn export_json<W: Write>(
|
|||||||
mut source: impl FnMut(NonZeroU64) -> (String, u32),
|
mut source: impl FnMut(NonZeroU64) -> (String, u32),
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Entry {
|
struct Entry<'a> {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
cat: &'static str,
|
cat: &'static str,
|
||||||
ph: &'static str,
|
ph: &'static str,
|
||||||
ts: f64,
|
ts: f64,
|
||||||
pid: u64,
|
pid: u64,
|
||||||
tid: u64,
|
tid: u64,
|
||||||
args: Option<Args>,
|
args: Option<Args<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Args {
|
struct Args<'a> {
|
||||||
file: String,
|
file: String,
|
||||||
line: u32,
|
line: u32,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
function_name: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
callsite_file: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
callsite_line: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
let lock = EVENTS.lock();
|
let lock = EVENTS.lock();
|
||||||
@ -128,7 +134,21 @@ pub fn export_json<W: Write>(
|
|||||||
ts: event.timestamp.micros_since(events[0].timestamp),
|
ts: event.timestamp.micros_since(events[0].timestamp),
|
||||||
pid: 1,
|
pid: 1,
|
||||||
tid: event.thread_id,
|
tid: event.thread_id,
|
||||||
args: event.span.map(&mut source).map(|(file, line)| Args { file, line }),
|
args: event.span.map(&mut source).map(|(file, line)| {
|
||||||
|
let (callsite_file, callsite_line) = match event.callsite.map(&mut source)
|
||||||
|
{
|
||||||
|
Some((a, b)) => (Some(a), Some(b)),
|
||||||
|
None => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
Args {
|
||||||
|
file,
|
||||||
|
line,
|
||||||
|
callsite_file,
|
||||||
|
callsite_line,
|
||||||
|
function_name: event.func.as_deref(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
.map_err(|e| format!("failed to serialize event: {e}"))?;
|
.map_err(|e| format!("failed to serialize event: {e}"))?;
|
||||||
}
|
}
|
||||||
@ -142,6 +162,8 @@ pub fn export_json<W: Write>(
|
|||||||
pub struct TimingScope {
|
pub struct TimingScope {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
span: Option<NonZeroU64>,
|
span: Option<NonZeroU64>,
|
||||||
|
callsite: Option<NonZeroU64>,
|
||||||
|
func: Option<String>,
|
||||||
thread_id: u64,
|
thread_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,14 +181,34 @@ impl TimingScope {
|
|||||||
/// `typst-timing`).
|
/// `typst-timing`).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_span(name: &'static str, span: Option<NonZeroU64>) -> Option<Self> {
|
pub fn with_span(name: &'static str, span: Option<NonZeroU64>) -> Option<Self> {
|
||||||
|
Self::with_callsite(name, span, None, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new scope with a span if timing is enabled.
|
||||||
|
///
|
||||||
|
/// The span is a raw number because `typst-timing` can't depend on
|
||||||
|
/// `typst-syntax` (or else `typst-syntax` couldn't depend on
|
||||||
|
/// `typst-timing`).
|
||||||
|
#[inline]
|
||||||
|
pub fn with_callsite(
|
||||||
|
name: &'static str,
|
||||||
|
span: Option<NonZeroU64>,
|
||||||
|
callsite: Option<NonZeroU64>,
|
||||||
|
func: Option<String>,
|
||||||
|
) -> Option<Self> {
|
||||||
if is_enabled() {
|
if is_enabled() {
|
||||||
return Some(Self::new_impl(name, span));
|
return Some(Self::new_impl(name, span, callsite, func));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new scope without checking if timing is enabled.
|
/// Create a new scope without checking if timing is enabled.
|
||||||
fn new_impl(name: &'static str, span: Option<NonZeroU64>) -> Self {
|
fn new_impl(
|
||||||
|
name: &'static str,
|
||||||
|
span: Option<NonZeroU64>,
|
||||||
|
callsite: Option<NonZeroU64>,
|
||||||
|
func: Option<String>,
|
||||||
|
) -> Self {
|
||||||
let (thread_id, timestamp) =
|
let (thread_id, timestamp) =
|
||||||
THREAD_DATA.with(|data| (data.id, Timestamp::now_with(data)));
|
THREAD_DATA.with(|data| (data.id, Timestamp::now_with(data)));
|
||||||
EVENTS.lock().push(Event {
|
EVENTS.lock().push(Event {
|
||||||
@ -174,9 +216,11 @@ impl TimingScope {
|
|||||||
timestamp,
|
timestamp,
|
||||||
name,
|
name,
|
||||||
span,
|
span,
|
||||||
|
callsite,
|
||||||
|
func: func.clone(),
|
||||||
thread_id,
|
thread_id,
|
||||||
});
|
});
|
||||||
Self { name, span, thread_id }
|
Self { name, span, callsite: None, thread_id, func }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +232,9 @@ impl Drop for TimingScope {
|
|||||||
timestamp,
|
timestamp,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
span: self.span,
|
span: self.span,
|
||||||
|
callsite: self.callsite,
|
||||||
thread_id: self.thread_id,
|
thread_id: self.thread_id,
|
||||||
|
func: std::mem::take(&mut self.func),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,6 +249,10 @@ struct Event {
|
|||||||
name: &'static str,
|
name: &'static str,
|
||||||
/// The raw value of the span of code that this event was recorded in.
|
/// The raw value of the span of code that this event was recorded in.
|
||||||
span: Option<NonZeroU64>,
|
span: Option<NonZeroU64>,
|
||||||
|
/// The raw value of the callsite span of the code that this event was recorded in.
|
||||||
|
callsite: Option<NonZeroU64>,
|
||||||
|
/// The function being called (if any).
|
||||||
|
func: Option<String>,
|
||||||
/// The thread ID of this event.
|
/// The thread ID of this event.
|
||||||
thread_id: u64,
|
thread_id: u64,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user