gluon_language_server/command/
completion.rs1use futures::channel::mpsc;
2
3use lsp_types::CompletionItem;
4
5use crate::completion;
6
7use lsp_types::{CompletionParams, CompletionResponse};
8
9use crate::{check_importer::Module, name::with_import, rpc::LanguageServerCommand, BoxFuture};
10
11use serde::Deserialize;
12use serde_json;
13
14use super::*;
15
16#[derive(Serialize, Deserialize)]
17pub struct CompletionData {
18 pub text_document_uri: Url,
19 pub position: Position,
20}
21
22#[derive(Clone)]
23struct Completion(RootedThread);
24impl LanguageServerCommand<CompletionParams> for Completion {
25 type Future = BoxFuture<Self::Output, ServerError<()>>;
26 type Output = Option<CompletionResponse>;
27 type Error = ();
28 fn execute(&self, change: CompletionParams) -> BoxFuture<Self::Output, ServerError<()>> {
29 let thread = self.0.clone();
30 let text_document_uri = change.text_document_position.text_document.uri.clone();
31 async move {
32 retrieve_expr(&thread.clone(), &text_document_uri, |module| {
33 let Module {
34 ref expr,
35 ref source,
36 ..
37 } = *module;
38
39 let expr = expr.expr();
40
41 let byte_index =
42 position_to_byte_index(&**source, &change.text_document_position.position)?;
43
44 let query = completion::SuggestionQuery {
45 modules: with_import(&thread, |import| {
46 import.modules(&mut thread.module_compiler(&mut thread.get_database()))
47 }),
48 ..completion::SuggestionQuery::default()
49 };
50
51 let db = thread.get_database();
52 let suggestions = query
53 .suggest(&db.as_env(), source.span(), expr, byte_index)
54 .into_iter()
55 .filter(|suggestion| !suggestion.name.starts_with("__"))
56 .collect::<Vec<_>>();
57
58 let mut items: Vec<_> = suggestions
59 .into_iter()
60 .map(|ident| {
61 let name: &str = ident.name.as_ref();
63 let label =
64 String::from(name.split(':').next().unwrap_or(ident.name.as_ref()));
65 CompletionItem {
66 insert_text: if label.starts_with(char::is_alphabetic) {
67 None
68 } else {
69 Some(format!("({})", label))
70 },
71 kind: Some(ident_to_completion_item_kind(&label, ident.typ.as_ref())),
72 label,
73 detail: match ident.typ {
74 either::Either::Right(ref typ) => match **typ {
75 Type::Hole => None,
76 _ => Some(format!("{}", ident.typ)),
77 },
78 either::Either::Left(_) => Some(format!("{}", ident.typ)),
79 },
80 data: Some(
81 serde_json::to_value(CompletionData {
82 text_document_uri: change
83 .text_document_position
84 .text_document
85 .uri
86 .clone(),
87 position: change.text_document_position.position,
88 })
89 .expect("CompletionData"),
90 ),
91 ..CompletionItem::default()
92 }
93 })
94 .collect();
95
96 items.sort_by(|l, r| l.label.cmp(&r.label));
97
98 Ok(Some(CompletionResponse::Array(items)))
99 })
100 .await
101 }
102 .boxed()
103 }
104
105 fn invalid_params(&self) -> Option<Self::Error> {
106 None
107 }
108}
109
110pub fn register(io: &mut IoHandler, thread: &RootedThread, message_log: &mpsc::Sender<String>) {
111 io.add_async_method(
112 request!("textDocument/completion"),
113 Completion(thread.clone()),
114 );
115
116 let thread = thread.clone();
117 let message_log = message_log.clone();
118 let resolve = move |mut item: CompletionItem| {
119 let thread = thread.clone();
120 let message_log = message_log.clone();
121 async move {
122 let data: CompletionData =
123 CompletionData::deserialize(item.data.as_ref().unwrap()).expect("CompletionData");
124
125 let message_log2 = message_log.clone();
126 let thread = thread.clone();
127 let label = item.label.clone();
128 log_message!(message_log.clone(), "{:?}", data.text_document_uri).await;
129
130 let comment = retrieve_expr_with_pos(
131 &thread,
132 &data.text_document_uri,
133 &data.position,
134 |module, byte_index| {
135 let db = thread.get_database();
136 let type_env = db.as_env();
137 let module_expr = module.expr.expr();
138 let (_, metadata_map) =
139 gluon::check::metadata::metadata(&type_env, module_expr);
140 Ok(completion::suggest_metadata(
141 &metadata_map,
142 &type_env,
143 module.source.span(),
144 module_expr,
145 byte_index,
146 &label,
147 )
148 .and_then(|metadata| metadata.comment.clone()))
149 },
150 )
151 .await?;
152
153 log_message!(message_log2, "{:?}", comment).await;
154
155 item.documentation = Some(make_documentation(
156 None::<&str>,
157 comment.as_ref().map_or("", |comment| &comment.content),
158 ));
159 Ok(item)
160 }
161 };
162 io.add_async_method(request!("completionItem/resolve"), resolve);
163}