Detailed changes
@@ -117,6 +117,7 @@ action!(Unfold);
action!(FoldSelectedRanges);
action!(Scroll, Vector2F);
action!(Select, SelectPhase);
+action!(ShowAutocomplete);
pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpener>>) {
path_openers.push(Box::new(items::BufferOpener));
@@ -224,6 +225,7 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpene
Binding::new("alt-cmd-[", Fold, Some("Editor")),
Binding::new("alt-cmd-]", Unfold, Some("Editor")),
Binding::new("alt-cmd-f", FoldSelectedRanges, Some("Editor")),
+ Binding::new("ctrl-shift-A", ShowAutocomplete, Some("Editor")),
]);
cx.add_action(Editor::open_new);
@@ -287,6 +289,7 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpene
cx.add_action(Editor::fold);
cx.add_action(Editor::unfold);
cx.add_action(Editor::fold_selected_ranges);
+ cx.add_action(Editor::show_autocomplete);
}
trait SelectionExt {
@@ -1495,6 +1498,15 @@ impl Editor {
}
}
+ fn show_autocomplete(&mut self, _: &ShowAutocomplete, cx: &mut ViewContext<Self>) {
+ let position = self
+ .newest_selection::<usize>(&self.buffer.read(cx).read(cx))
+ .head();
+ self.buffer
+ .update(cx, |buffer, cx| buffer.completions(position, cx))
+ .detach_and_log_err(cx);
+ }
+
pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
self.start_transaction(cx);
self.select_all(&SelectAll, cx);
@@ -5,6 +5,7 @@ use anyhow::Result;
use clock::ReplicaId;
use collections::{HashMap, HashSet};
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
+pub use language::Completion;
use language::{
Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Outline,
OutlineItem, Selection, ToOffset as _, ToPoint as _, TransactionId,
@@ -847,6 +848,18 @@ impl MultiBuffer {
})
}
+ pub fn completions<T>(
+ &self,
+ position: T,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<Vec<Completion>>>
+ where
+ T: ToOffset,
+ {
+ let (buffer, text_anchor) = self.text_anchor_for_position(position, cx);
+ buffer.update(cx, |buffer, cx| buffer.completions(text_anchor, cx))
+ }
+
pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
self.buffers
.borrow()
@@ -12,6 +12,7 @@ use crate::{
use anyhow::{anyhow, Result};
use clock::ReplicaId;
use futures::FutureExt as _;
+use fuzzy::StringMatchCandidate;
use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task};
use lazy_static::lazy_static;
use lsp::LanguageServer;
@@ -114,6 +115,12 @@ pub struct Diagnostic {
pub is_disk_based: bool,
}
+pub struct Completion {
+ old_range: Range<Anchor>,
+ new_text: String,
+ lsp_completion: lsp::CompletionItem,
+}
+
struct LanguageServerState {
server: Arc<LanguageServer>,
latest_snapshot: watch::Sender<Option<LanguageServerSnapshot>>,
@@ -1611,6 +1618,96 @@ impl Buffer {
false
}
}
+
+ pub fn completions<T>(
+ &self,
+ position: T,
+ cx: &mut ModelContext<Self>,
+ ) -> Task<Result<Vec<Completion>>>
+ where
+ T: ToOffset,
+ {
+ let file = if let Some(file) = self.file.as_ref() {
+ file
+ } else {
+ return Task::ready(Ok(Default::default()));
+ };
+
+ if let Some(file) = file.as_local() {
+ let server = if let Some(lang) = self.language_server.as_ref() {
+ lang.server.clone()
+ } else {
+ return Task::ready(Ok(Default::default()));
+ };
+ let abs_path = file.abs_path(cx);
+ let position = self.offset_to_point_utf16(position.to_offset(self));
+
+ cx.spawn(|this, mut cx| async move {
+ let t0 = Instant::now();
+ let completions = server
+ .request::<lsp::request::Completion>(lsp::CompletionParams {
+ text_document_position: lsp::TextDocumentPositionParams::new(
+ lsp::TextDocumentIdentifier::new(
+ lsp::Url::from_file_path(abs_path).unwrap(),
+ ),
+ position.to_lsp_position(),
+ ),
+ context: Default::default(),
+ work_done_progress_params: Default::default(),
+ partial_result_params: Default::default(),
+ })
+ .await?;
+ dbg!("completions", t0.elapsed());
+ // fuzzy::match_strings(candidates, query, smart_case, max_results, cancel_flag, background)
+
+ let mut completions = if let Some(completions) = completions {
+ match completions {
+ lsp::CompletionResponse::Array(completions) => completions,
+ lsp::CompletionResponse::List(list) => list.items,
+ }
+ } else {
+ Default::default()
+ };
+
+ this.update(&mut cx, |this, cx| {
+ this.edit([0..0], "use std::sync::Arc;\n", cx)
+ });
+
+ let mut futures = Vec::new();
+ for completion in completions {
+ futures.push(server.request::<lsp::request::ResolveCompletionItem>(completion));
+ }
+
+ let completions = futures::future::try_join_all(futures).await?;
+ dbg!("resolution", t0.elapsed(), completions);
+ // let candidates = completions
+ // .iter()
+ // .enumerate()
+ // .map(|(id, completion)| {
+ // let text = completion
+ // .filter_text
+ // .clone()
+ // .unwrap_or_else(|| completion.label.clone());
+ // StringMatchCandidate::new(id, text)
+ // })
+ // .collect::<Vec<_>>();
+ // let matches = fuzzy::match_strings(
+ // &candidates,
+ // "Arc",
+ // false,
+ // 100,
+ // &Default::default(),
+ // cx.background(),
+ // )
+ // .await;
+ // dbg!(matches);
+
+ Ok(Default::default())
+ })
+ } else {
+ Task::ready(Ok(Default::default()))
+ }
+ }
}
#[cfg(any(test, feature = "test-support"))]