@@ -11,7 +11,7 @@ use once_cell::sync::Lazy;
1111
1212use crate::{
1313 compositor::{Callback, Component, Compositor},
14− ui::{self, Picker, Popup, Prompt, PromptEvent},
14+ ui::{self, Completion, Picker, Popup, Prompt, PromptEvent},
1515};
1616
1717use std::path::PathBuf;
@@ -55,12 +55,7 @@ impl<'a> Context<'a> {
5555 /// Push a new component onto the compositor.
5656 pub fn push_layer(&mut self, mut component: Box<dyn Component>) {
5757 self.callback = Some(Box::new(
58− |compositor: &mut Compositor, editor: &mut Editor| {
59− let size = compositor.size();
60− // trigger required_size on init
61− component.required_size((size.width, size.height));
62− compositor.push(component);
63− },
58+ |compositor: &mut Compositor, editor: &mut Editor| compositor.push(component),
6459 ));
6560 }
6661
@@ -1604,10 +1599,28 @@ pub fn completion(cx: &mut Context) {
16041599 // // downcast dyn Component to Completion component
16051600 // // emit response to completion (completion.complete/handle(response))
16061601 // })
1607− // async {
1608− // let (response, callback) = response.await?;
1609− // callback(response)
1610− // }
1602+ //
1603+ // typing after prompt opens: usually start offset is tracked and everything between
1604+ // start_offset..cursor is replaced. For our purposes we could keep the start state (doc,
1605+ // selection) and revert to them before applying. This needs to properly reset changes/history
1606+ // though...
1607+ //
1608+ // company-mode does this by matching the prefix of the completion and removing it.
1609+
1610+ // ignore isIncomplete for now
1611+ // keep state while typing
1612+ // the behavior should be, filter the menu based on input
1613+ // if items returns empty at any point, remove the popup
1614+ // if backspace past initial offset point, remove the popup
1615+ //
1616+ // debounce requests!
1617+ //
1618+ // need an idle timeout thing.
1619+ // https://github.com/company-mode/company-mode/blob/master/company.el#L620-L622
1620+ //
1621+ // "The idle delay in seconds until completion starts automatically.
1622+ // The prefix still has to satisfy `company-minimum-prefix-length' before that
1623+ // happens. The value of nil means no idle completion."
16111624
16121625 let doc = cx.doc();
16131626
@@ -1623,11 +1636,13 @@ pub fn completion(cx: &mut Context) {
16231636
16241637 let res = smol::block_on(language_server.completion(doc.identifier(), pos)).unwrap();
16251638
1639+ let trigger_offset = doc.selection().cursor();
1640+
16261641 cx.callback(
16271642 res,
1628− |editor: &mut Editor,
1629− compositor: &mut Compositor,
1630− response: Option<lsp::CompletionResponse>| {
1643+ move |editor: &mut Editor,
1644+ compositor: &mut Compositor,
1645+ response: Option<lsp::CompletionResponse>| {
16311646 let items = match response {
16321647 Some(lsp::CompletionResponse::Array(items)) => items,
16331648 // TODO: do something with is_incomplete
@@ -1640,92 +1655,11 @@ pub fn completion(cx: &mut Context) {
16401655
16411656 // TODO: if no completion, show some message or something
16421657 if !items.is_empty() {
1643− // let snapshot = doc.state.clone();
1644− let mut menu = ui::Menu::new(
1645− items,
1646− |item| {
1647− // format_fn
1648− item.label.as_str().into()
1649−
1650− // TODO: use item.filter_text for filtering
1651− },
1652− move |editor: &mut Editor, item, event| {
1653− match event {
1654− PromptEvent::Abort => {
1655− // revert state
1656− // let id = editor.view().doc;
1657− // let doc = &mut editor.documents[id];
1658− // doc.state = snapshot.clone();
1659− }
1660− PromptEvent::Validate => {
1661− let id = editor.view().doc;
1662− let doc = &mut editor.documents[id];
1663−
1664− // revert state to what it was before the last update
1665− // doc.state = snapshot.clone();
1666−
1667− // extract as fn(doc, item):
1668−
1669− // TODO: need to apply without composing state...
1670− // TODO: need to update lsp on accept/cancel by diffing the snapshot with
1671− // the final state?
1672− // -> on update simply update the snapshot, then on accept redo the call,
1673− // finally updating doc.changes + notifying lsp.
1674− //
1675− // or we could simply use doc.undo + apply when changing between options
1676−
1677− // always present here
1678− let item = item.unwrap();
1679−
1680− use helix_lsp::{lsp, util};
1681− // determine what to insert: text_edit | insert_text | label
1682− let edit = if let Some(edit) = &item.text_edit {
1683− match edit {
1684− lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
1685− lsp::CompletionTextEdit::InsertAndReplace(item) => {
1686− unimplemented!(
1687− "completion: insert_and_replace {:?}",
1688− item
1689− )
1690− }
1691− }
1692− } else {
1693− item.insert_text.as_ref().unwrap_or(&item.label);
1694− unimplemented!();
1695− // lsp::TextEdit::new(); TODO: calculate a TextEdit from insert_text
1696− // and we insert at position.
1697− };
1698−
1699− // TODO: merge edit with additional_text_edits
1700− if let Some(additional_edits) = &item.additional_text_edits {
1701− if !additional_edits.is_empty() {
1702− unimplemented!(
1703− "completion: additional_text_edits: {:?}",
1704− additional_edits
1705− );
1706− }
1707− }
1708−
1709− let transaction =
1710− util::generate_transaction_from_edits(doc.text(), vec![edit]);
1711− doc.apply(&transaction);
1712− // TODO: doc.append_changes_to_history(); if not in insert mode?
1713− }
1714− _ => (),
1715− };
1716− },
1717− );
1718−
1719− let popup = Popup::new(Box::new(menu));
1720− let mut component: Box<dyn Component> = Box::new(popup);
1658+ let completion = Completion::new(items, trigger_offset);
17211659
17221660 // Server error: content modified
17231661
1724− // TODO: this is shared with cx.push_layer
1725− let size = compositor.size();
1726− // trigger required_size on init
1727− component.required_size((size.width, size.height));
1728− compositor.push(component);
1662+ compositor.push(Box::new(completion));
17291663 }
17301664 },
17311665 );
@@ -1774,7 +1708,7 @@ pub fn hover(cx: &mut Context) {
17741708 // skip if contents empty
17751709
17761710 let contents = ui::Markdown::new(contents);
1777− let mut popup = Popup::new(Box::new(contents));
1711+ let mut popup = Popup::new(contents);
17781712 cx.push_layer(Box::new(popup));
17791713 }
17801714}