use tower_lsp::lsp_types::*;
pub fn inline_values_in_range(source: &str, range: Range) -> Vec<InlineValue> {
let mut result = Vec::new();
let is_ident_start = |c: char| c.is_alphabetic() || c == '_';
let is_ident_cont = |c: char| c.is_alphanumeric() || c == '_';
for (line_idx, line) in source.lines().enumerate() {
let line_num = line_idx as u32;
if line_num < range.start.line || line_num > range.end.line {
continue;
}
let line_min_col: Option<u32> =
(line_num == range.start.line).then_some(range.start.character);
let line_max_col: Option<u32> = (line_num == range.end.line).then_some(range.end.character);
let chars: Vec<(u32, char)> = {
let mut out = Vec::with_capacity(line.len());
let mut col: u32 = 0;
for ch in line.chars() {
out.push((col, ch));
col += ch.len_utf16() as u32;
}
out
};
let mut i = 0usize;
while i < chars.len() {
if chars[i].1 != '$' {
i += 1;
continue;
}
if chars.get(i + 1).map(|(_, c)| *c) == Some('$') {
i += 2;
continue;
}
let dollar_col = chars[i].0;
i += 1;
let Some(&(_, first)) = chars.get(i) else {
continue;
};
if !is_ident_start(first) {
continue;
}
let name_start_idx = i;
while i < chars.len() && is_ident_cont(chars[i].1) {
i += 1;
}
let name_end_idx = i;
let var_name: String = chars[name_start_idx..name_end_idx]
.iter()
.map(|(_, c)| *c)
.collect();
if var_name == "this" {
continue;
}
let end_col = chars.get(name_end_idx).map(|(c, _)| *c).unwrap_or_else(|| {
chars
.last()
.map(|(c, ch)| c + ch.len_utf16() as u32)
.unwrap_or(0)
});
if let Some(min) = line_min_col
&& dollar_col < min
{
continue;
}
if let Some(max) = line_max_col
&& end_col > max
{
continue;
}
result.push(InlineValue::VariableLookup(InlineValueVariableLookup {
range: Range {
start: Position {
line: line_num,
character: dollar_col,
},
end: Position {
line: line_num,
character: end_col,
},
},
variable_name: Some(var_name),
case_sensitive_lookup: true,
}));
}
}
result
}