Files
oak-gpui/crates/language/src/highlight_map.rs
T
Agus Zubiaga 8561943095 rust: Nicer snippet completions (#43891)
At some point, rust-analyzer started including the snippet expression
for some completions in the description field, but that can look like a
bug:

<img width="1570" height="578" alt="CleanShot 2025-11-27 at 20 39 49@2x"
src="https://github.com/user-attachments/assets/5a87c9fe-c0a8-472f-8d83-3bc9e9e00bbc"></img>


In these cases, we will now inline the tab stops as an ellipsis
character:

<img width="1544" height="428" alt="CleanShot 2025-12-01 at 10 01 18@2x"
src="https://github.com/user-attachments/assets/4c550891-4545-47cd-a295-a5eb07e78e92"></img>

You may also notice that we now syntax highlight the pattern closer to
what it looks like after accepted.

Alternatively, when the tab stop isn't just one position, it gets
highlighted as a selection since that's what it would do when accepted:

<img width="1558" height="314" alt="CleanShot 2025-12-01 at 10 04 37@2x"
src="https://github.com/user-attachments/assets/ce630ab2-da22-4072-a996-7b71ba21637d"
/>


Release Notes:

- rust: Display completion tab stops inline rather than as a raw LSP
snippet expression
2025-12-01 12:19:00 -03:00

115 lines
3.5 KiB
Rust

use gpui::HighlightStyle;
use std::sync::Arc;
use theme::SyntaxTheme;
#[derive(Clone, Debug)]
pub struct HighlightMap(Arc<[HighlightId]>);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HighlightId(pub u32);
const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
impl HighlightMap {
pub(crate) fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
// For each capture name in the highlight query, find the longest
// key in the theme's syntax styles that matches all of the
// dot-separated components of the capture name.
HighlightMap(
capture_names
.iter()
.map(|capture_name| {
theme
.highlights
.iter()
.enumerate()
.filter_map(|(i, (key, _))| {
let mut len = 0;
let capture_parts = capture_name.split('.');
for key_part in key.split('.') {
if capture_parts.clone().any(|part| part == key_part) {
len += 1;
} else {
return None;
}
}
Some((i, len))
})
.max_by_key(|(_, len)| *len)
.map_or(DEFAULT_SYNTAX_HIGHLIGHT_ID, |(i, _)| HighlightId(i as u32))
})
.collect(),
)
}
pub fn get(&self, capture_id: u32) -> HighlightId {
self.0
.get(capture_id as usize)
.copied()
.unwrap_or(DEFAULT_SYNTAX_HIGHLIGHT_ID)
}
}
impl HighlightId {
pub const TABSTOP_INSERT_ID: HighlightId = HighlightId(u32::MAX - 1);
pub const TABSTOP_REPLACE_ID: HighlightId = HighlightId(u32::MAX - 2);
pub(crate) fn is_default(&self) -> bool {
*self == DEFAULT_SYNTAX_HIGHLIGHT_ID
}
pub fn style(&self, theme: &SyntaxTheme) -> Option<HighlightStyle> {
theme.highlights.get(self.0 as usize).map(|entry| entry.1)
}
pub fn name<'a>(&self, theme: &'a SyntaxTheme) -> Option<&'a str> {
theme.highlights.get(self.0 as usize).map(|e| e.0.as_str())
}
}
impl Default for HighlightMap {
fn default() -> Self {
Self(Arc::new([]))
}
}
impl Default for HighlightId {
fn default() -> Self {
DEFAULT_SYNTAX_HIGHLIGHT_ID
}
}
#[cfg(test)]
mod tests {
use super::*;
use gpui::rgba;
#[test]
fn test_highlight_map() {
let theme = SyntaxTheme {
highlights: [
("function", rgba(0x100000ff)),
("function.method", rgba(0x200000ff)),
("function.async", rgba(0x300000ff)),
("variable.builtin.self.rust", rgba(0x400000ff)),
("variable.builtin", rgba(0x500000ff)),
("variable", rgba(0x600000ff)),
]
.iter()
.map(|(name, color)| (name.to_string(), (*color).into()))
.collect(),
};
let capture_names = &[
"function.special",
"function.async.rust",
"variable.builtin.self",
];
let map = HighlightMap::new(capture_names, &theme);
assert_eq!(map.get(0).name(&theme), Some("function"));
assert_eq!(map.get(1).name(&theme), Some("function.async"));
assert_eq!(map.get(2).name(&theme), Some("variable.builtin"));
}
}