Skip to content

Commit

Permalink
Showing 17 changed files with 216 additions and 18 deletions.
3 changes: 2 additions & 1 deletion crates/tinymist-query/src/fixtures/inlay_hints/base.typ
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#text("")
#let f(x, y) = x + y
#f(1, 2)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#let f(x, y) = [#(x + y)];
$lr(#f(1, 2))$
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$lr(1, 2, 3)$
1 change: 1 addition & 0 deletions crates/tinymist-query/src/fixtures/inlay_hints/named.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#table(align: left)[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#text("")
#text(red, "")
#text(18pt, red, "")
Original file line number Diff line number Diff line change
@@ -6,10 +6,18 @@ input_file: crates/tinymist-query/src/fixtures/inlay_hints/base.typ
[
{
"kind": 2,
"label": ": body",
"label": ": x",
"position": {
"character": 8,
"line": 0
"character": 4,
"line": 1
}
},
{
"kind": 2,
"label": ": y",
"position": {
"character": 7,
"line": 1
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
source: crates/tinymist-query/src/inlay_hint.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/inlay_hints/math_markup_mod.typ
---
[
{
"kind": 2,
"label": ": x",
"position": {
"character": 8,
"line": 1
}
},
{
"kind": 2,
"label": ": y",
"position": {
"character": 11,
"line": 1
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: crates/tinymist-query/src/inlay_hint.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/inlay_hints/math_mod.typ
---
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: crates/tinymist-query/src/inlay_hint.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/inlay_hints/named.typ
---
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: crates/tinymist-query/src/inlay_hint.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/inlay_hints/named_or_pos.typ
---
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: crates/tinymist-query/src/inlay_hint.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/inlay_hints/unique_positional.typ
---
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#text("")
#align(left)[]
Original file line number Diff line number Diff line change
@@ -5,9 +5,9 @@ input_file: crates/tinymist-query/src/fixtures/references/at_def.typ
---
[
{
"range": "2:2:2:3"
"range": "1:2:1:3"
},
{
"range": "1:2:1:3"
"range": "2:2:2:3"
}
]
Original file line number Diff line number Diff line change
@@ -4,9 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/references/redefine.typ
---
[
{
"range": "8:9:8:10"
},
{
"range": "3:2:3:3"
},
@@ -15,5 +12,8 @@ input_file: crates/tinymist-query/src/fixtures/references/redefine.typ
},
{
"range": "6:12:6:13"
},
{
"range": "8:9:8:10"
}
]
143 changes: 134 additions & 9 deletions crates/tinymist-query/src/inlay_hint.rs
Original file line number Diff line number Diff line change
@@ -11,6 +11,34 @@ use typst_ts_core::typst::prelude::eco_vec;

use crate::prelude::*;

pub struct InlayHintConfig {
// positional arguments group
pub on_pos_args: bool,
pub off_single_pos_arg: bool,

// variadic arguments group
pub on_variadic_args: bool,
pub only_first_variadic_args: bool,

// todo
// The typst sugar grammar
pub on_content_block_args: bool,
}

impl InlayHintConfig {
pub const fn smart() -> Self {
Self {
on_pos_args: true,
off_single_pos_arg: true,

on_variadic_args: true,
only_first_variadic_args: true,

on_content_block_args: true,
}
}
}

#[derive(Debug, Clone)]
pub struct InlayHintRequest {
pub path: PathBuf,
@@ -26,7 +54,7 @@ impl InlayHintRequest {
let source = get_suitable_source_in_workspace(world, &self.path).ok()?;
let range = lsp_to_typst::range(self.range, position_encoding, &source)?;

let hints = inlay_hints(world, &source, range, position_encoding).ok()?;
let hints = inlay_hint(world, &source, range, position_encoding).ok()?;
debug!(
"got inlay hints on {source:?} => {hints:?}",
source = source.id(),
@@ -41,12 +69,14 @@ impl InlayHintRequest {
}
}

fn inlay_hints(
fn inlay_hint(
world: &TypstSystemWorld,
source: &Source,
range: Range<usize>,
encoding: PositionEncoding,
) -> FileResult<Vec<InlayHint>> {
const SMART: InlayHintConfig = InlayHintConfig::smart();

struct InlayHintWorker<'a> {
world: &'a TypstSystemWorld,
source: &'a Source,
@@ -110,10 +140,48 @@ fn inlay_hints(
Value::Func(f) => Some(f),
_ => None,
})?;
trace!("got function {func:?}");
log::info!("got function {func:?}");

let call_info = analyze_call(func, args)?;
trace!("got call_info {call_info:?}");
log::info!("got call_info {call_info:?}");

let check_single_pos_arg = || {
let mut pos = 0;
let mut content_pos = 0;

for arg in args.items() {
let Some(arg_node) = args_node.find(arg.span()) else {
continue;
};

let Some(info) = call_info.arg_mapping.get(&arg_node) else {
continue;
};

if info.kind != ParamKind::Named {
if info.is_content_block {
content_pos += 1;
} else {
pos += 1;
};

if pos > 1 && content_pos > 1 {
break;
}
}
}

(pos <= 1, content_pos <= 1)
};

let (disable_by_single_pos_arg, disable_by_single_content_pos_arg) =
if SMART.on_pos_args && SMART.off_single_pos_arg {
check_single_pos_arg()
} else {
(false, false)
};

let mut is_first_variadic_arg = true;

for arg in args.items() {
let Some(arg_node) = args_node.find(arg.span()) else {
@@ -128,6 +196,36 @@ fn inlay_hints(
continue;
}

match info.kind {
ParamKind::Named => {
continue;
}
ParamKind::Positional
if call_info.signature.has_fill_or_size_or_stroke =>
{
continue
}
ParamKind::Positional
if !SMART.on_pos_args
|| (info.is_content_block
&& disable_by_single_content_pos_arg)
|| (!info.is_content_block && disable_by_single_pos_arg) =>
{
continue
}
ParamKind::Rest
if (!SMART.on_variadic_args
|| (!is_first_variadic_arg
&& SMART.only_first_variadic_args)) =>
{
continue;
}
ParamKind::Rest => {
is_first_variadic_arg = false;
}
ParamKind::Positional => {}
}

let pos = arg_node.range().end;
let lsp_pos =
typst_to_lsp::offset_to_position(pos, self.encoding, self.source);
@@ -186,12 +284,14 @@ enum ParamKind {
#[derive(Debug, Clone)]
struct CallParamInfo {
kind: ParamKind,
is_content_block: bool,
param: Arc<ParamSpec>,
// types: EcoVec<()>,
}

#[derive(Debug, Clone)]
struct CallInfo {
signature: Arc<Signature>,
arg_mapping: HashMap<SyntaxNode, CallParamInfo>,
}

@@ -201,10 +301,6 @@ fn analyze_call(func: Func, args: ast::Args<'_>) -> Option<Arc<CallInfo>> {
}

fn analyze_call_no_cache(func: Func, args: ast::Args<'_>) -> Option<CallInfo> {
let mut info = CallInfo {
arg_mapping: HashMap::new(),
};

#[derive(Debug, Clone)]
enum ArgValue<'a> {
Instance(Args),
@@ -223,6 +319,11 @@ fn analyze_call_no_cache(func: Func, args: ast::Args<'_>) -> Option<CallInfo> {
let signature = analyze_signature(func);
trace!("got signature {signature:?}");

let mut info = CallInfo {
arg_mapping: HashMap::new(),
signature: signature.clone(),
};

enum PosState {
Init,
Pos(usize),
@@ -265,10 +366,13 @@ fn analyze_call_no_cache(func: Func, args: ast::Args<'_>) -> Option<CallInfo> {
};

if let Some(arg) = arg {
// todo: process desugar
let is_content_block = arg.kind() == SyntaxKind::ContentBlock;
info.arg_mapping.insert(
arg,
CallParamInfo {
kind,
is_content_block,
param: param.clone(),
// types: eco_vec![],
},
@@ -296,10 +400,13 @@ fn analyze_call_no_cache(func: Func, args: ast::Args<'_>) -> Option<CallInfo> {
};

if let Some(arg) = arg {
// todo: process desugar
let is_content_block = arg.kind() == SyntaxKind::ContentBlock;
info.arg_mapping.insert(
arg,
CallParamInfo {
kind: ParamKind::Rest,
is_content_block,
param: rest.clone(),
// types: eco_vec![],
},
@@ -333,6 +440,7 @@ fn analyze_call_no_cache(func: Func, args: ast::Args<'_>) -> Option<CallInfo> {
arg_tag,
CallParamInfo {
kind: ParamKind::Named,
is_content_block: false,
param: param.clone(),
// types: eco_vec![],
},
@@ -386,6 +494,7 @@ impl ParamSpec {
struct Signature {
pos: Vec<Arc<ParamSpec>>,
named: HashMap<Cow<'static, str>, Arc<ParamSpec>>,
has_fill_or_size_or_stroke: bool,
rest: Option<Arc<ParamSpec>>,
_broken: bool,
}
@@ -406,9 +515,24 @@ fn analyze_signature(func: Func) -> Arc<Signature> {
let mut named = HashMap::new();
let mut rest = None;
let mut broken = false;
let mut has_fill = false;
let mut has_stroke = false;
let mut has_size = false;

for param in params.into_iter() {
if param.named {
match param.name.as_ref() {
"fill" => {
has_fill = true;
}
"stroke" => {
has_stroke = true;
}
"size" => {
has_size = true;
}
_ => {}
}
named.insert(param.name.clone(), param.clone());
}

@@ -429,6 +553,7 @@ fn analyze_signature(func: Func) -> Arc<Signature> {
pos,
named,
rest,
has_fill_or_size_or_stroke: has_fill || has_stroke || has_size,
_broken: broken,
})
}
@@ -502,7 +627,7 @@ mod tests {
use crate::tests::*;

#[test]
fn test() {
fn smart() {
snapshot_testing("inlay_hints", &|world, path| {
let source = get_suitable_source_in_workspace(world, &path).unwrap();

8 changes: 8 additions & 0 deletions crates/tinymist-query/src/references.rs
Original file line number Diff line number Diff line change
@@ -119,6 +119,14 @@ mod tests {
};

let result = request.request(world, PositionEncoding::Utf16);
// sort
let result = result.map(|mut e| {
e.sort_by(|a, b| match a.range.start.cmp(&b.range.start) {
std::cmp::Ordering::Equal => a.range.end.cmp(&b.range.end),
e => e,
});
e
});
assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
});
}

0 comments on commit 61be2f7

Please sign in to comment.