@@ -12,11 +12,63 @@ use helix_core::{Position, Transaction};
12use helix_view::Editor;
12use helix_view::Editor;
14use crate::commands;
14use crate::commands;
15−use crate::ui::{Markdown, Menu, Popup, PromptEvent};
15+use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
17use helix_lsp::lsp;
17use helix_lsp::lsp;
18use lsp::CompletionItem;
18use lsp::CompletionItem;
20+impl menu::Item for CompletionItem {
21+ fn filter_text(&self) -> &str {
24+ .unwrap_or_else(|| &self.label)
28+ fn label(&self) -> &str {
32+ fn row(&self) -> menu::Row {
34+ menu::Cell::from(self.label.as_str()),
35+ menu::Cell::from(match self.kind {
36+ Some(lsp::CompletionItemKind::Text) => "text",
37+ Some(lsp::CompletionItemKind::Method) => "method",
38+ Some(lsp::CompletionItemKind::Function) => "function",
39+ Some(lsp::CompletionItemKind::Constructor) => "constructor",
40+ Some(lsp::CompletionItemKind::Field) => "field",
41+ Some(lsp::CompletionItemKind::Variable) => "variable",
42+ Some(lsp::CompletionItemKind::Class) => "class",
43+ Some(lsp::CompletionItemKind::Interface) => "interface",
44+ Some(lsp::CompletionItemKind::Module) => "module",
45+ Some(lsp::CompletionItemKind::Property) => "property",
46+ Some(lsp::CompletionItemKind::Unit) => "unit",
47+ Some(lsp::CompletionItemKind::Value) => "value",
48+ Some(lsp::CompletionItemKind::Enum) => "enum",
49+ Some(lsp::CompletionItemKind::Keyword) => "keyword",
50+ Some(lsp::CompletionItemKind::Snippet) => "snippet",
51+ Some(lsp::CompletionItemKind::Color) => "color",
52+ Some(lsp::CompletionItemKind::File) => "file",
53+ Some(lsp::CompletionItemKind::Reference) => "reference",
54+ Some(lsp::CompletionItemKind::Folder) => "folder",
55+ Some(lsp::CompletionItemKind::EnumMember) => "enum_member",
56+ Some(lsp::CompletionItemKind::Constant) => "constant",
57+ Some(lsp::CompletionItemKind::Struct) => "struct",
58+ Some(lsp::CompletionItemKind::Event) => "event",
59+ Some(lsp::CompletionItemKind::Operator) => "operator",
60+ Some(lsp::CompletionItemKind::TypeParameter) => "type_param",
63+ // self.detail.as_deref().unwrap_or("")
64+ // self.label_details
66+ // .or(self.detail())
20/// Wraps a Menu.
72/// Wraps a Menu.
21pub struct Completion {
73pub struct Completion {
22 popup: Popup<Menu<CompletionItem>>, // TODO: Popup<Menu> need to be able to access contents.
74 popup: Popup<Menu<CompletionItem>>, // TODO: Popup<Menu> need to be able to access contents.
@@ -31,92 +83,83 @@ impl Completion {
31 trigger_offset: usize,
83 trigger_offset: usize,
32 ) -> Self {
84 ) -> Self {
33 // let items: Vec<CompletionItem> = Vec::new();
85 // let items: Vec<CompletionItem> = Vec::new();
34− let mut menu = Menu::new(
38− item.label.as_str().into()
40− // TODO: use item.filter_text for filtering
42− move |editor: &mut Editor, item, event| {
86+ let mut menu = Menu::new(items, move |editor: &mut Editor, item, event| {
43− match event {
87+ match event {
44− PromptEvent::Abort => {
88+ PromptEvent::Abort => {
45− // revert state
89+ // revert state
46− // let id = editor.view().doc;
90+ // let id = editor.view().doc;
47− // let doc = &mut editor.documents[id];
91+ // let doc = &mut editor.documents[id];
48− // doc.state = snapshot.clone();
92+ // doc.state = snapshot.clone();
50− PromptEvent::Validate => {
94+ PromptEvent::Validate => {
51− let (view, doc) = editor.current();
95+ let (view, doc) = editor.current();
53− // revert state to what it was before the last update
97+ // revert state to what it was before the last update
54− // doc.state = snapshot.clone();
98+ // doc.state = snapshot.clone();
56− // extract as fn(doc, item):
100+ // extract as fn(doc, item):
58− // TODO: need to apply without composing state...
102+ // TODO: need to apply without composing state...
59− // TODO: need to update lsp on accept/cancel by diffing the snapshot with
103+ // TODO: need to update lsp on accept/cancel by diffing the snapshot with
60− // the final state?
104+ // the final state?
61− // -> on update simply update the snapshot, then on accept redo the call,
105+ // -> on update simply update the snapshot, then on accept redo the call,
62− // finally updating doc.changes + notifying lsp.
106+ // finally updating doc.changes + notifying lsp.
64− // or we could simply use doc.undo + apply when changing between options
108+ // or we could simply use doc.undo + apply when changing between options
66− // always present here
110+ // always present here
67− let item = item.unwrap();
111+ let item = item.unwrap();
69− use helix_lsp::{lsp, util};
113+ use helix_lsp::{lsp, util};
70− // determine what to insert: text_edit | insert_text | label
114+ // determine what to insert: text_edit | insert_text | label
71− let edit = if let Some(edit) = &item.text_edit {
115+ let edit = if let Some(edit) = &item.text_edit {
72− match edit {
116+ match edit {
73− lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
117+ lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
74− lsp::CompletionTextEdit::InsertAndReplace(item) => {
118+ lsp::CompletionTextEdit::InsertAndReplace(item) => {
75− unimplemented!("completion: insert_and_replace {:?}", item)
119+ unimplemented!("completion: insert_and_replace {:?}", item)
79− item.insert_text.as_ref().unwrap_or(&item.label);
81− // lsp::TextEdit::new(); TODO: calculate a TextEdit from insert_text
82− // and we insert at position.
85− // if more text was entered, remove it
86− let cursor = doc.selection(view.id).cursor();
87− if trigger_offset < cursor {
88− let remove = Transaction::change(
90− vec![(trigger_offset, cursor, None)].into_iter(),
92− doc.apply(&remove, view.id);
123+ item.insert_text.as_ref().unwrap_or(&item.label);
125+ // lsp::TextEdit::new(); TODO: calculate a TextEdit from insert_text
126+ // and we insert at position.
95− use helix_lsp::OffsetEncoding;
129+ // if more text was entered, remove it
96− let transaction = util::generate_transaction_from_edits(
130+ let cursor = doc.selection(view.id).cursor();
131+ if trigger_offset < cursor {
132+ let remove = Transaction::change(
97 doc.text(),
133 doc.text(),
98− vec![edit],
134+ vec![(trigger_offset, cursor, None)].into_iter(),
99− offset_encoding, // TODO: should probably transcode in Client
136+ doc.apply(&remove, view.id);
139+ use helix_lsp::OffsetEncoding;
140+ let transaction = util::generate_transaction_from_edits(
143+ offset_encoding, // TODO: should probably transcode in Client
101− doc.apply(&transaction, view.id);
145+ doc.apply(&transaction, view.id);
103− // TODO: merge edit with additional_text_edits
147+ // TODO: merge edit with additional_text_edits
104− if let Some(additional_edits) = &item.additional_text_edits {
148+ if let Some(additional_edits) = &item.additional_text_edits {
105− // gopls uses this to add extra imports
149+ // gopls uses this to add extra imports
106− if !additional_edits.is_empty() {
150+ if !additional_edits.is_empty() {
107− let transaction = util::generate_transaction_from_edits(
151+ let transaction = util::generate_transaction_from_edits(
108− doc.text(),
152+ doc.text(),
109− additional_edits.clone(),
153+ additional_edits.clone(),
110− offset_encoding, // TODO: should probably transcode in Client
154+ offset_encoding, // TODO: should probably transcode in Client
112− doc.apply(&transaction, view.id);
156+ doc.apply(&transaction, view.id);
116− _ => (),
160+ _ => (),
120 let popup = Popup::new(menu);
163 let popup = Popup::new(menu);