use bynk_check::hints::{Hint, HintKind};
use bynk_syntax::span::Span;
use tower_lsp::lsp_types::*;
use crate::position::offset_to_position;
pub fn inlay_hints(text: &str, hints: &[Hint], requested: Span) -> Vec<InlayHint> {
hints
.iter()
.filter_map(|h| {
let anchor = match h.kind {
HintKind::Type => h.span.end,
HintKind::Parameter => h.span.start,
};
(requested.start <= anchor && anchor <= requested.end).then(|| {
let (kind, padding_right) = match h.kind {
HintKind::Type => (InlayHintKind::TYPE, None),
HintKind::Parameter => (InlayHintKind::PARAMETER, Some(true)),
};
InlayHint {
position: offset_to_position(text, anchor),
label: InlayHintLabel::String(h.label.clone()),
kind: Some(kind),
text_edits: None,
tooltip: None,
padding_left: None,
padding_right,
data: None,
}
})
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn label_of(h: &InlayHint) -> &str {
match &h.label {
InlayHintLabel::String(s) => s,
other => panic!("expected a plain string label, got {other:?}"),
}
}
fn type_hint(start: usize, end: usize, label: &str) -> Hint {
Hint {
span: Span::new(start, end),
label: label.to_string(),
kind: HintKind::Type,
}
}
#[test]
fn type_hints_anchor_after_the_name_with_no_padding() {
let text = "let x = 1\nlet y = 2\n";
let hints = vec![type_hint(4, 5, ": Int"), type_hint(14, 15, ": Int")];
let got = inlay_hints(text, &hints, Span::new(0, text.len()));
assert_eq!(got.len(), 2);
assert_eq!(got[0].position, Position::new(0, 5));
assert_eq!(label_of(&got[0]), ": Int");
assert_eq!(got[0].kind, Some(InlayHintKind::TYPE));
assert_eq!(got[0].padding_right, None);
assert_eq!(got[1].position, Position::new(1, 5));
}
#[test]
fn parameter_hints_anchor_before_the_argument_with_trailing_padding() {
let text = "f(5)\n";
let hints = vec![Hint {
span: Span::new(2, 3),
label: "count:".to_string(),
kind: HintKind::Parameter,
}];
let got = inlay_hints(text, &hints, Span::new(0, text.len()));
assert_eq!(got.len(), 1);
assert_eq!(got[0].position, Position::new(0, 2));
assert_eq!(label_of(&got[0]), "count:");
assert_eq!(got[0].kind, Some(InlayHintKind::PARAMETER));
assert_eq!(got[0].padding_right, Some(true));
}
#[test]
fn out_of_range_hints_are_filtered() {
let text = "let x = 1\nlet y = 2\n";
let hints = vec![type_hint(4, 5, ": Int"), type_hint(14, 15, ": Int")];
let got = inlay_hints(text, &hints, Span::new(0, 9));
assert_eq!(got.len(), 1);
assert_eq!(got[0].position, Position::new(0, 5));
}
#[test]
fn empty_hint_set_returns_empty() {
assert!(inlay_hints("let x = 1\n", &[], Span::new(0, 9)).is_empty());
}
}