feat: expand completion snippets and navigate placeholders
This commit is contained in:
+81
-1
@@ -340,6 +340,7 @@ fn translate(key: KeyEvent) -> Option<Input> {
|
||||
KeyCode::Home => Input::Home,
|
||||
KeyCode::End => Input::End,
|
||||
KeyCode::Tab => Input::Char('\t'),
|
||||
KeyCode::BackTab => Input::BackTab,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -683,7 +684,11 @@ fn draw(
|
||||
}
|
||||
}
|
||||
}
|
||||
for selection in selections.iter().filter(|_| !editor.visual_linewise()) {
|
||||
for selection in selections
|
||||
.iter()
|
||||
.filter(|_| !editor.visual_linewise())
|
||||
.chain(editor.snippet_selections())
|
||||
{
|
||||
let first = selection.start.row.max(*top);
|
||||
let last = selection.end.row.min(top.saturating_add(height - 1));
|
||||
let style = Style::default().fg(Color::White).bg(Color::Blue);
|
||||
@@ -1583,6 +1588,8 @@ fn semantic_style(token: &SemanticToken) -> Style {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
// These fixtures intentionally contain ranges, not lists of indices.
|
||||
#[allow(clippy::single_range_in_vec_init)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::{
|
||||
@@ -1643,6 +1650,7 @@ mod tests {
|
||||
detail: Some("\tTAIL".into()),
|
||||
primary_edit: 0,
|
||||
edits: vec![],
|
||||
snippet: None,
|
||||
})
|
||||
.collect(),
|
||||
selected: 20,
|
||||
@@ -1707,6 +1715,78 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snippet_highlight_tracks_argument_graphemes_without_filling_the_row() {
|
||||
use crate::plugin::{
|
||||
CompletionEdit, CompletionItem, CompletionSnippet, SnippetTabstop, TextPosition,
|
||||
};
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
editor.handle(Input::Char('i'));
|
||||
editor
|
||||
.show_completion(vec![CompletionItem {
|
||||
label: "call".into(),
|
||||
detail: None,
|
||||
primary_edit: 0,
|
||||
edits: vec![CompletionEdit {
|
||||
start: TextPosition { row: 0, column: 0 },
|
||||
end: TextPosition { row: 0, column: 0 },
|
||||
text: "call(界e\u{301}, tail)".into(),
|
||||
}],
|
||||
snippet: Some(CompletionSnippet {
|
||||
tabstops: vec![
|
||||
SnippetTabstop {
|
||||
index: 1,
|
||||
ranges: vec![5..11],
|
||||
ancestors: vec![vec![]],
|
||||
},
|
||||
SnippetTabstop {
|
||||
index: 2,
|
||||
ranges: vec![13..17],
|
||||
ancestors: vec![vec![]],
|
||||
},
|
||||
SnippetTabstop {
|
||||
index: 0,
|
||||
ranges: vec![18..18],
|
||||
ancestors: vec![vec![]],
|
||||
},
|
||||
],
|
||||
}),
|
||||
}])
|
||||
.unwrap();
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(28, 6)).unwrap();
|
||||
let mut viewports = BufferViewports::default();
|
||||
for (key, selected, caret) in [
|
||||
(None, 7..10, Position::new(7, 0)),
|
||||
(Some(Input::Char('\t')), 12..16, Position::new(12, 0)),
|
||||
] {
|
||||
if let Some(key) = key {
|
||||
editor.handle(key);
|
||||
}
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &editor))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
// A wide glyph's leading cell owns its terminal style.
|
||||
for x in (0..28).filter(|x| *x != 8) {
|
||||
assert_eq!(
|
||||
buffer[(x, 0)].bg == Color::Blue,
|
||||
selected.contains(&x),
|
||||
"column {x}, selection {:?}, screen:\n{}",
|
||||
editor.snippet_selections(),
|
||||
screen_text(&terminal),
|
||||
);
|
||||
}
|
||||
assert_eq!(terminal.get_cursor_position().unwrap(), caret);
|
||||
}
|
||||
editor.handle(Input::Escape);
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, &editor))
|
||||
.unwrap();
|
||||
for x in 0..28 {
|
||||
assert_ne!(terminal.backend().buffer()[(x, 0)].bg, Color::Blue);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_shows_more_documentation_with_markdown_and_fenced_code_colors() {
|
||||
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user