4 // not sure why - but extending Gtk.SourceCompletionProvider seems to give an error..
7 public class CompletionProvider : Object, GtkSource.CompletionProvider
10 public JsRender.JsRender file {
11 get { return this.editor.file; }
15 //public WindowState windowstate;
16 public CompletionModel model;
17 global::Gtk.StringFilter filter;
19 public CompletionProvider(Editor editor)
23 // this.windowstate = null; // not ready until the UI is built.
27 public string get_name ()
29 return "roojsbuilder";
32 public int get_priority (GtkSource.CompletionContext context)
37 public void activate (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal)
39 GLib.debug("compelte activate");
40 var p = (CompletionProposal) proposal;
41 global::Gtk.TextMark end_mark = null;
42 global::Gtk.TextIter begin, end;
44 if (!context.get_bounds(out begin, out end)) {
47 var buffer = begin.get_buffer();
53 /* If the insertion cursor is within a word and the trailing characters
54 * of the word match the suffix of the proposal, then limit how much
55 * text we insert so that the word is completed properly.
57 if (!end.ends_line() &&
58 !end.get_char().isspace() &&
63 if (word_end.forward_word_end ()) {
64 var text = end.get_slice(word_end);
65 if (text.length > word.length) {
68 if (word.has_suffix (text)) {
69 //g_assert (strlen (word) >= strlen (text));
70 len = word.length - text.length;
71 end_mark = buffer.create_mark (null, word_end, false);
76 buffer.begin_user_action();
77 buffer.delete (ref begin, ref end);
78 buffer.insert ( ref begin, word, len);
79 buffer.end_user_action ();
83 buffer.get_iter_at_mark(out end, end_mark);
84 buffer.select_range(end, end);
85 buffer.delete_mark(end_mark);
92 public void display (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal, GtkSource.CompletionCell cell)
94 GLib.debug("compelte display");
95 var col = cell.get_column();
97 var p = (CompletionProposal) proposal;
99 case GtkSource.CompletionColumn.TYPED_TEXT:
100 cell.set_text(p.label);
102 case GtkSource.CompletionColumn.ICON:
103 cell.set_icon_name("completion-snippet-symbolic");
105 case GtkSource.CompletionColumn.COMMENT:
106 cell.set_text(p.info);
108 case GtkSource.CompletionColumn.DETAILS:
109 cell.set_text(p.text);
119 internal async GLib.ListModel populate_async (GtkSource.CompletionContext context, GLib.Cancellable? cancellable)
121 GLib.debug("pupoulate async");
123 global::Gtk.TextIter begin, end;
124 Lsp.CompletionList res;
125 if (context.get_bounds (out begin, out end)) {
126 yield this.file.getLanguageServer().completion(this.file, end.get_line(), end.get_line_offset(), 1, out res);
130 this.model = new CompletionModel(this, context, res, cancellable);
131 var word = context.get_word();
134 var expression = new global::Gtk.PropertyExpression(typeof(CompletionProposal), null, "label");
135 this.filter = new global::Gtk.StringFilter(expression);
136 this.filter.set_search( word);
137 var filter_model = new global::Gtk.FilterListModel(this.model, this.filter);
138 filter.match_mode = global::Gtk.StringFilterMatchMode.PREFIX;
139 filter_model.set_incremental(true);
146 internal void refilter (GtkSource.CompletionContext context, GLib.ListModel in_model)
149 GLib.debug("pupoulate refilter");
152 var word = context.get_word();
153 this.filter.set_search(word);
160 public bool activate_proposal (GtkSource.CompletionProposal proposal, TextIter iter)
163 istart.backward_find_char(is_space, null);
164 istart.forward_char();
166 // var search = iter.get_text(istart);
168 var buffer = iter.get_buffer();
169 buffer.delete(ref istart, ref iter);
170 buffer.insert(ref istart, proposal.get_text(), -1);
177 private bool is_space(unichar space){
178 return space.isspace() || space.to_string() == "";
183 public class CompletionModel : Object, GLib.ListModel
185 CompletionProvider provider;
186 Gee.ArrayList<CompletionProposal> items;
188 int minimum_word_size = 2;
190 public Cancellable? cancellable;
192 public CompletionModel(CompletionProvider provider, GtkSource.CompletionContext context, Lsp.CompletionList? res, Cancellable? cancellable)
194 this.provider = provider;
195 this.cancellable = cancellable;
196 this.items = new Gee.ArrayList<CompletionProposal>();
198 var word = context.get_word();
199 GLib.debug("looking for %s", word);
202 foreach(var comp in res.items) {
204 this.items.add(new CompletionProposal(comp));
208 print("GOT %d results\n", (int) items.size);
210 if (this.items.size < this.minimum_word_size) {
214 items.sort((a, b) => {
215 return ((string)(a.text)).collate((string)(b.text));
222 public GLib.Object? get_item (uint pos)
224 return (Object) this.items.get((int) pos);
226 public GLib.Type get_item_type ()
228 return typeof(GtkSource.CompletionProposal);
230 public uint get_n_items ()
232 return this.items.size;
234 public bool can_filter (string word)
236 if (word == null || word[0] == 0) {
240 if (word.length < this.minimum_word_size) {
244 /* If the new word starts with our initial word, then we can simply
245 * refilter outside this model using a GtkFilterListModel.
248 return word.has_prefix(this.search);
250 public void cancel ()
252 if (this.cancellable != null) {
253 this.cancellable.cancel();
260 public class CompletionProposal : Object, GtkSource.CompletionProposal
263 public string label { get; set; default = ""; }
265 public string text { get; set; default = ""; }
266 public string info { get; set; default = ""; }
267 public CompletionProposal(Lsp.CompletionItem ci) //string label, string text, string info)
271 this.text = ci.detail == null ? "" : ci.detail ;
272 this.label = ci.label;
273 this.info = ci.documentation == null ? "": ci.documentation.value;
274 GLib.debug("SET: text=%s, label = %s; info =%s", ci.detail, ci.label, "to long..");