mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 01:34:02 +00:00
Start work on syntax highlighting completions
This commit is contained in:
parent
45898daf83
commit
439d12cb85
7 changed files with 349 additions and 221 deletions
|
@ -20,7 +20,7 @@ use gpui::{
|
|||
color::Color,
|
||||
elements::*,
|
||||
executor,
|
||||
fonts::TextStyle,
|
||||
fonts::{self, HighlightStyle, TextStyle},
|
||||
geometry::vector::{vec2f, Vector2F},
|
||||
keymap::Binding,
|
||||
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
|
||||
|
@ -489,7 +489,7 @@ impl CompletionState {
|
|||
});
|
||||
|
||||
for mat in &mut matches {
|
||||
let filter_start = self.completions[mat.candidate_id].filter_range().start;
|
||||
let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
|
||||
for position in &mut mat.positions {
|
||||
*position += filter_start;
|
||||
}
|
||||
|
@ -1628,7 +1628,7 @@ impl Editor {
|
|||
.map(|(id, completion)| {
|
||||
StringMatchCandidate::new(
|
||||
id,
|
||||
completion.lsp_completion.label[completion.filter_range()].into(),
|
||||
completion.label.text[completion.label.filter_range.clone()].into(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
|
@ -1710,15 +1710,6 @@ impl Editor {
|
|||
move |range, items, cx| {
|
||||
let settings = build_settings(cx);
|
||||
let start_ix = range.start;
|
||||
let label_style = LabelStyle {
|
||||
text: settings.style.text.clone(),
|
||||
highlight_text: settings
|
||||
.style
|
||||
.text
|
||||
.clone()
|
||||
.highlight(settings.style.autocomplete.match_highlight, cx.font_cache())
|
||||
.log_err(),
|
||||
};
|
||||
for (ix, mat) in matches[range].iter().enumerate() {
|
||||
let item_style = if start_ix + ix == selected_item {
|
||||
settings.style.autocomplete.selected_item
|
||||
|
@ -1727,8 +1718,20 @@ impl Editor {
|
|||
};
|
||||
let completion = &completions[mat.candidate_id];
|
||||
items.push(
|
||||
Label::new(completion.label().to_string(), label_style.clone())
|
||||
.with_highlights(mat.positions.clone())
|
||||
Text::new(completion.label.text.clone(), settings.style.text.clone())
|
||||
.with_soft_wrap(false)
|
||||
.with_highlights(combine_syntax_and_fuzzy_match_highlights(
|
||||
&completion.label.text,
|
||||
settings.style.text.color.into(),
|
||||
completion.label.runs.iter().filter_map(
|
||||
|(range, highlight_id)| {
|
||||
highlight_id
|
||||
.style(&settings.style.syntax)
|
||||
.map(|style| (range.clone(), style))
|
||||
},
|
||||
),
|
||||
&mat.positions,
|
||||
))
|
||||
.contained()
|
||||
.with_style(item_style)
|
||||
.boxed(),
|
||||
|
@ -1742,7 +1745,11 @@ impl Editor {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|(_, mat)| {
|
||||
state.completions[mat.candidate_id].label().chars().count()
|
||||
state.completions[mat.candidate_id]
|
||||
.label
|
||||
.text
|
||||
.chars()
|
||||
.count()
|
||||
})
|
||||
.map(|(ix, _)| ix),
|
||||
)
|
||||
|
@ -4699,6 +4706,77 @@ pub fn settings_builder(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn combine_syntax_and_fuzzy_match_highlights(
|
||||
text: &str,
|
||||
default_style: HighlightStyle,
|
||||
syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
|
||||
match_indices: &[usize],
|
||||
) -> Vec<(Range<usize>, HighlightStyle)> {
|
||||
let mut result = Vec::new();
|
||||
let mut match_indices = match_indices.iter().copied().peekable();
|
||||
|
||||
for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
|
||||
{
|
||||
syntax_highlight.font_properties.weight(Default::default());
|
||||
|
||||
// Add highlights for any fuzzy match characters before the next
|
||||
// syntax highlight range.
|
||||
while let Some(&match_index) = match_indices.peek() {
|
||||
if match_index >= range.start {
|
||||
break;
|
||||
}
|
||||
match_indices.next();
|
||||
let end_index = char_ix_after(match_index, text);
|
||||
let mut match_style = default_style;
|
||||
match_style.font_properties.weight(fonts::Weight::BOLD);
|
||||
result.push((match_index..end_index, match_style));
|
||||
}
|
||||
|
||||
if range.start == usize::MAX {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add highlights for any fuzzy match characters within the
|
||||
// syntax highlight range.
|
||||
let mut offset = range.start;
|
||||
while let Some(&match_index) = match_indices.peek() {
|
||||
if match_index >= range.end {
|
||||
break;
|
||||
}
|
||||
|
||||
match_indices.next();
|
||||
if match_index > offset {
|
||||
result.push((offset..match_index, syntax_highlight));
|
||||
}
|
||||
|
||||
let mut end_index = char_ix_after(match_index, text);
|
||||
while let Some(&next_match_index) = match_indices.peek() {
|
||||
if next_match_index == end_index && next_match_index < range.end {
|
||||
end_index = char_ix_after(next_match_index, text);
|
||||
match_indices.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut match_style = syntax_highlight;
|
||||
match_style.font_properties.weight(fonts::Weight::BOLD);
|
||||
result.push((match_index..end_index, match_style));
|
||||
offset = end_index;
|
||||
}
|
||||
|
||||
if offset < range.end {
|
||||
result.push((offset..range.end, syntax_highlight));
|
||||
}
|
||||
}
|
||||
|
||||
fn char_ix_after(ix: usize, text: &str) -> usize {
|
||||
ix + text[ix..].chars().next().unwrap().len_utf8()
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -7327,6 +7405,76 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_combine_syntax_and_fuzzy_match_highlights() {
|
||||
let string = "abcdefghijklmnop";
|
||||
let default = HighlightStyle::default();
|
||||
let syntax_ranges = [
|
||||
(
|
||||
0..3,
|
||||
HighlightStyle {
|
||||
color: Color::red(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
4..8,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
];
|
||||
let match_indices = [4, 6, 7, 8];
|
||||
assert_eq!(
|
||||
combine_syntax_and_fuzzy_match_highlights(
|
||||
&string,
|
||||
default,
|
||||
syntax_ranges.into_iter(),
|
||||
&match_indices,
|
||||
),
|
||||
&[
|
||||
(
|
||||
0..3,
|
||||
HighlightStyle {
|
||||
color: Color::red(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
4..5,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
5..6,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
6..8,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
8..9,
|
||||
HighlightStyle {
|
||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
||||
..default
|
||||
},
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
|
||||
let point = DisplayPoint::new(row as u32, column as u32);
|
||||
point..point
|
||||
|
|
|
@ -7,7 +7,7 @@ pub use crate::{
|
|||
use crate::{
|
||||
diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
|
||||
outline::OutlineItem,
|
||||
range_from_lsp, Outline, ToLspPosition,
|
||||
range_from_lsp, CompletionLabel, Outline, ToLspPosition,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use clock::ReplicaId;
|
||||
|
@ -114,7 +114,7 @@ pub struct Diagnostic {
|
|||
pub struct Completion<T> {
|
||||
pub old_range: Range<T>,
|
||||
pub new_text: String,
|
||||
pub label: Option<String>,
|
||||
pub label: CompletionLabel,
|
||||
pub lsp_completion: lsp::CompletionItem,
|
||||
}
|
||||
|
||||
|
@ -1829,7 +1829,7 @@ impl Buffer {
|
|||
Some(Completion {
|
||||
old_range: this.anchor_before(old_range.start)..this.anchor_after(old_range.end),
|
||||
new_text,
|
||||
label: language.as_ref().and_then(|l| l.label_for_completion(&lsp_completion)),
|
||||
label: language.as_ref().and_then(|l| l.label_for_completion(&lsp_completion)).unwrap_or_else(|| CompletionLabel::plain(&lsp_completion)),
|
||||
lsp_completion,
|
||||
})
|
||||
} else {
|
||||
|
@ -2664,28 +2664,12 @@ impl Default for Diagnostic {
|
|||
}
|
||||
|
||||
impl<T> Completion<T> {
|
||||
pub fn label(&self) -> &str {
|
||||
self.label.as_deref().unwrap_or(&self.lsp_completion.label)
|
||||
}
|
||||
|
||||
pub fn filter_range(&self) -> Range<usize> {
|
||||
if let Some(filter_text) = self.lsp_completion.filter_text.as_deref() {
|
||||
if let Some(start) = self.label().find(filter_text) {
|
||||
start..start + filter_text.len()
|
||||
} else {
|
||||
0..self.label().len()
|
||||
}
|
||||
} else {
|
||||
0..self.label().len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort_key(&self) -> (usize, &str) {
|
||||
let kind_key = match self.lsp_completion.kind {
|
||||
Some(lsp::CompletionItemKind::VARIABLE) => 0,
|
||||
_ => 1,
|
||||
};
|
||||
(kind_key, &self.label()[self.filter_range()])
|
||||
(kind_key, &self.label.text[self.label.filter_range.clone()])
|
||||
}
|
||||
|
||||
pub fn is_snippet(&self) -> bool {
|
||||
|
|
|
@ -5,7 +5,7 @@ use theme::SyntaxTheme;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct HighlightMap(Arc<[HighlightId]>);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct HighlightId(pub u32);
|
||||
|
||||
const DEFAULT_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
|
||||
|
|
|
@ -49,11 +49,23 @@ pub trait ToLspPosition {
|
|||
|
||||
pub trait LspPostProcessor: 'static + Send + Sync {
|
||||
fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams);
|
||||
fn label_for_completion(&self, _completion: &lsp::CompletionItem) -> Option<String> {
|
||||
fn label_for_completion(
|
||||
&self,
|
||||
_: &lsp::CompletionItem,
|
||||
_: &Language,
|
||||
) -> Option<CompletionLabel> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CompletionLabel {
|
||||
pub text: String,
|
||||
pub runs: Vec<(Range<usize>, HighlightId)>,
|
||||
pub filter_range: Range<usize>,
|
||||
pub left_aligned_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
pub struct LanguageConfig {
|
||||
pub name: String,
|
||||
|
@ -253,24 +265,26 @@ impl Language {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn label_for_completion(&self, completion: &lsp::CompletionItem) -> Option<String> {
|
||||
pub fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
) -> Option<CompletionLabel> {
|
||||
self.lsp_post_processor
|
||||
.as_ref()
|
||||
.and_then(|p| p.label_for_completion(completion))
|
||||
.as_ref()?
|
||||
.label_for_completion(completion, self)
|
||||
}
|
||||
|
||||
pub fn highlight_text<'a>(&'a self, text: &'a Rope) -> Vec<(Range<usize>, HighlightId)> {
|
||||
pub fn highlight_text<'a>(
|
||||
&'a self,
|
||||
text: &'a Rope,
|
||||
range: Range<usize>,
|
||||
) -> Vec<(Range<usize>, HighlightId)> {
|
||||
let mut result = Vec::new();
|
||||
if let Some(grammar) = &self.grammar {
|
||||
let tree = grammar.parse_text(text, None);
|
||||
let mut offset = 0;
|
||||
for chunk in BufferChunks::new(
|
||||
text,
|
||||
0..text.len(),
|
||||
Some(&tree),
|
||||
self.grammar.as_ref(),
|
||||
vec![],
|
||||
) {
|
||||
for chunk in BufferChunks::new(text, range, Some(&tree), self.grammar.as_ref(), vec![])
|
||||
{
|
||||
let end_offset = offset + chunk.text.len();
|
||||
if let Some(highlight_id) = chunk.highlight_id {
|
||||
result.push((offset..end_offset, highlight_id));
|
||||
|
@ -291,6 +305,10 @@ impl Language {
|
|||
HighlightMap::new(grammar.highlights_query.capture_names(), theme);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grammar(&self) -> Option<&Arc<Grammar>> {
|
||||
self.grammar.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Grammar {
|
||||
|
@ -316,6 +334,28 @@ impl Grammar {
|
|||
pub fn highlight_map(&self) -> HighlightMap {
|
||||
self.highlight_map.lock().clone()
|
||||
}
|
||||
|
||||
pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> {
|
||||
let capture_id = self.highlights_query.capture_index_for_name(name)?;
|
||||
Some(self.highlight_map.lock().get(capture_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl CompletionLabel {
|
||||
fn plain(completion: &lsp::CompletionItem) -> Self {
|
||||
let mut result = Self {
|
||||
text: completion.label.clone(),
|
||||
runs: Vec::new(),
|
||||
left_aligned_len: completion.label.len(),
|
||||
filter_range: 0..completion.label.len(),
|
||||
};
|
||||
if let Some(filter_text) = &completion.filter_text {
|
||||
if let Some(ix) = completion.label.find(filter_text) {
|
||||
result.filter_range = ix..ix + filter_text.len();
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::{diagnostic_set::DiagnosticEntry, Completion, Diagnostic, Language, Operation};
|
||||
use crate::{
|
||||
diagnostic_set::DiagnosticEntry, Completion, CompletionLabel, Diagnostic, Language, Operation,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use clock::ReplicaId;
|
||||
use collections::HashSet;
|
||||
|
@ -403,7 +405,9 @@ pub fn deserialize_completion(
|
|||
Ok(Completion {
|
||||
old_range: old_start..old_end,
|
||||
new_text: completion.new_text,
|
||||
label: language.and_then(|l| l.label_for_completion(&lsp_completion)),
|
||||
label: language
|
||||
.and_then(|l| l.label_for_completion(&lsp_completion))
|
||||
.unwrap_or(CompletionLabel::plain(&lsp_completion)),
|
||||
lsp_completion,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use editor::{
|
||||
display_map::ToDisplayPoint, Anchor, AnchorRangeExt, Autoscroll, DisplayPoint, Editor,
|
||||
EditorSettings, ToPoint,
|
||||
combine_syntax_and_fuzzy_match_highlights, display_map::ToDisplayPoint, Anchor, AnchorRangeExt,
|
||||
Autoscroll, DisplayPoint, Editor, EditorSettings, ToPoint,
|
||||
};
|
||||
use fuzzy::StringMatch;
|
||||
use gpui::{
|
||||
action,
|
||||
elements::*,
|
||||
fonts::{self, HighlightStyle},
|
||||
geometry::vector::Vector2F,
|
||||
keymap::{self, Binding},
|
||||
AppContext, Axis, Entity, MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
|
||||
|
@ -17,7 +16,6 @@ use ordered_float::OrderedFloat;
|
|||
use postage::watch;
|
||||
use std::{
|
||||
cmp::{self, Reverse},
|
||||
ops::Range,
|
||||
sync::Arc,
|
||||
};
|
||||
use workspace::{
|
||||
|
@ -362,7 +360,7 @@ impl OutlineView {
|
|||
.with_highlights(combine_syntax_and_fuzzy_match_highlights(
|
||||
&outline_item.text,
|
||||
style.label.text.clone().into(),
|
||||
&outline_item.highlight_ranges,
|
||||
outline_item.highlight_ranges.iter().cloned(),
|
||||
&string_match.positions,
|
||||
))
|
||||
.contained()
|
||||
|
@ -372,153 +370,3 @@ impl OutlineView {
|
|||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
fn combine_syntax_and_fuzzy_match_highlights(
|
||||
text: &str,
|
||||
default_style: HighlightStyle,
|
||||
syntax_ranges: &[(Range<usize>, HighlightStyle)],
|
||||
match_indices: &[usize],
|
||||
) -> Vec<(Range<usize>, HighlightStyle)> {
|
||||
let mut result = Vec::new();
|
||||
let mut match_indices = match_indices.iter().copied().peekable();
|
||||
|
||||
for (range, mut syntax_highlight) in syntax_ranges
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain([(usize::MAX..0, Default::default())])
|
||||
{
|
||||
syntax_highlight.font_properties.weight(Default::default());
|
||||
|
||||
// Add highlights for any fuzzy match characters before the next
|
||||
// syntax highlight range.
|
||||
while let Some(&match_index) = match_indices.peek() {
|
||||
if match_index >= range.start {
|
||||
break;
|
||||
}
|
||||
match_indices.next();
|
||||
let end_index = char_ix_after(match_index, text);
|
||||
let mut match_style = default_style;
|
||||
match_style.font_properties.weight(fonts::Weight::BOLD);
|
||||
result.push((match_index..end_index, match_style));
|
||||
}
|
||||
|
||||
if range.start == usize::MAX {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add highlights for any fuzzy match characters within the
|
||||
// syntax highlight range.
|
||||
let mut offset = range.start;
|
||||
while let Some(&match_index) = match_indices.peek() {
|
||||
if match_index >= range.end {
|
||||
break;
|
||||
}
|
||||
|
||||
match_indices.next();
|
||||
if match_index > offset {
|
||||
result.push((offset..match_index, syntax_highlight));
|
||||
}
|
||||
|
||||
let mut end_index = char_ix_after(match_index, text);
|
||||
while let Some(&next_match_index) = match_indices.peek() {
|
||||
if next_match_index == end_index && next_match_index < range.end {
|
||||
end_index = char_ix_after(next_match_index, text);
|
||||
match_indices.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut match_style = syntax_highlight;
|
||||
match_style.font_properties.weight(fonts::Weight::BOLD);
|
||||
result.push((match_index..end_index, match_style));
|
||||
offset = end_index;
|
||||
}
|
||||
|
||||
if offset < range.end {
|
||||
result.push((offset..range.end, syntax_highlight));
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn char_ix_after(ix: usize, text: &str) -> usize {
|
||||
ix + text[ix..].chars().next().unwrap().len_utf8()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::{color::Color, fonts::HighlightStyle};
|
||||
|
||||
#[test]
|
||||
fn test_combine_syntax_and_fuzzy_match_highlights() {
|
||||
let string = "abcdefghijklmnop";
|
||||
let default = HighlightStyle::default();
|
||||
let syntax_ranges = [
|
||||
(
|
||||
0..3,
|
||||
HighlightStyle {
|
||||
color: Color::red(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
4..8,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
];
|
||||
let match_indices = [4, 6, 7, 8];
|
||||
assert_eq!(
|
||||
combine_syntax_and_fuzzy_match_highlights(
|
||||
&string,
|
||||
default,
|
||||
&syntax_ranges,
|
||||
&match_indices,
|
||||
),
|
||||
&[
|
||||
(
|
||||
0..3,
|
||||
HighlightStyle {
|
||||
color: Color::red(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
4..5,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
5..6,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
6..8,
|
||||
HighlightStyle {
|
||||
color: Color::green(),
|
||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
||||
..default
|
||||
},
|
||||
),
|
||||
(
|
||||
8..9,
|
||||
HighlightStyle {
|
||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
||||
..default
|
||||
},
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,18 +32,36 @@ impl LspPostProcessor for RustPostProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
fn label_for_completion(&self, completion: &lsp::CompletionItem) -> Option<String> {
|
||||
fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Language,
|
||||
) -> Option<CompletionLabel> {
|
||||
let detail = completion.detail.as_ref()?;
|
||||
match completion.kind {
|
||||
Some(
|
||||
lsp::CompletionItemKind::CONSTANT
|
||||
| lsp::CompletionItemKind::FIELD
|
||||
| lsp::CompletionItemKind::VARIABLE,
|
||||
) => {
|
||||
let mut label = completion.label.clone();
|
||||
label.push_str(": ");
|
||||
label.push_str(detail);
|
||||
Some(label)
|
||||
Some(lsp::CompletionItemKind::FIELD) => {
|
||||
let name = &completion.label;
|
||||
let text = format!("{}: {}", name, detail);
|
||||
let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
|
||||
let runs = language.highlight_text(&source, 11..11 + text.len());
|
||||
return Some(CompletionLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..name.len(),
|
||||
left_aligned_len: name.len(),
|
||||
});
|
||||
}
|
||||
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE) => {
|
||||
let name = &completion.label;
|
||||
let text = format!("{}: {}", name, detail);
|
||||
let source = Rope::from(format!("let {} = ();", text).as_str());
|
||||
let runs = language.highlight_text(&source, 4..4 + text.len());
|
||||
return Some(CompletionLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..name.len(),
|
||||
left_aligned_len: name.len(),
|
||||
});
|
||||
}
|
||||
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD) => {
|
||||
lazy_static! {
|
||||
|
@ -51,13 +69,20 @@ impl LspPostProcessor for RustPostProcessor {
|
|||
}
|
||||
|
||||
if detail.starts_with("fn(") {
|
||||
Some(REGEX.replace(&completion.label, &detail[2..]).to_string())
|
||||
} else {
|
||||
None
|
||||
let text = REGEX.replace(&completion.label, &detail[2..]).to_string();
|
||||
let source = Rope::from(format!("fn {} {{}}", text).as_str());
|
||||
let runs = language.highlight_text(&source, 3..3 + text.len());
|
||||
return Some(CompletionLabel {
|
||||
left_aligned_len: text.find("->").unwrap_or(text.len()),
|
||||
filter_range: 0..completion.label.find('(').unwrap_or(text.len()),
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,9 +125,10 @@ fn load_query(path: &str) -> Cow<'static, str> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::color::Color;
|
||||
use language::LspPostProcessor;
|
||||
|
||||
use super::RustPostProcessor;
|
||||
use theme::SyntaxTheme;
|
||||
|
||||
#[test]
|
||||
fn test_process_rust_diagnostics() {
|
||||
|
@ -144,4 +170,82 @@ mod tests {
|
|||
"cannot borrow `self.d` as mutable\n`self` is a `&` reference"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_rust_completions() {
|
||||
let language = rust();
|
||||
let grammar = language.grammar().unwrap();
|
||||
let theme = SyntaxTheme::new(vec![
|
||||
("type".into(), Color::green().into()),
|
||||
("keyword".into(), Color::blue().into()),
|
||||
("function".into(), Color::red().into()),
|
||||
("property".into(), Color::white().into()),
|
||||
]);
|
||||
|
||||
language.set_theme(&theme);
|
||||
|
||||
let highlight_function = grammar.highlight_id_for_name("function").unwrap();
|
||||
let highlight_type = grammar.highlight_id_for_name("type").unwrap();
|
||||
let highlight_keyword = grammar.highlight_id_for_name("keyword").unwrap();
|
||||
let highlight_field = grammar.highlight_id_for_name("property").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
language.label_for_completion(&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::FUNCTION),
|
||||
label: "hello(…)".to_string(),
|
||||
detail: Some("fn(&mut Option<T>) -> Vec<T>".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
Some(CompletionLabel {
|
||||
text: "hello(&mut Option<T>) -> Vec<T>".to_string(),
|
||||
filter_range: 0..5,
|
||||
runs: vec![
|
||||
(0..5, highlight_function),
|
||||
(7..10, highlight_keyword),
|
||||
(11..17, highlight_type),
|
||||
(18..19, highlight_type),
|
||||
(25..28, highlight_type),
|
||||
(29..30, highlight_type),
|
||||
],
|
||||
left_aligned_len: 22,
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
language.label_for_completion(&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::FIELD),
|
||||
label: "len".to_string(),
|
||||
detail: Some("usize".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
Some(CompletionLabel {
|
||||
text: "len: usize".to_string(),
|
||||
filter_range: 0..3,
|
||||
runs: vec![(0..3, highlight_field), (5..10, highlight_type),],
|
||||
left_aligned_len: 3,
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
language.label_for_completion(&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::FUNCTION),
|
||||
label: "hello(…)".to_string(),
|
||||
detail: Some("fn(&mut Option<T>) -> Vec<T>".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
Some(CompletionLabel {
|
||||
text: "hello(&mut Option<T>) -> Vec<T>".to_string(),
|
||||
filter_range: 0..5,
|
||||
runs: vec![
|
||||
(0..5, highlight_function),
|
||||
(7..10, highlight_keyword),
|
||||
(11..17, highlight_type),
|
||||
(18..19, highlight_type),
|
||||
(25..28, highlight_type),
|
||||
(29..30, highlight_type),
|
||||
],
|
||||
left_aligned_len: 22,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue