fix line numbering issues with vala generator - hopefully fixes completion in node...
[roobuilder] / src / Palete / CompletionProvider.vala
1  
2 //using Gtk;
3
4 // not sure why - but extending Gtk.SourceCompletionProvider seems to give an error..
5 namespace Palete {
6
7     public class CompletionProvider : Object, GtkSource.CompletionProvider
8     {
9
10                 public JsRender.JsRender file {
11                         get { return this.editor.file; }
12                         private set {}
13                 }
14                 public Editor editor; 
15                 //public WindowState windowstate;
16                 public CompletionModel model;
17                 global::Gtk.StringFilter filter;
18
19                 public CompletionProvider(Editor editor)
20                 {
21                     this.editor  = editor;
22                  
23                    // this.windowstate = null; // not ready until the UI is built.
24                     
25                 }
26
27                 public string get_name ()
28                 {
29                   return  "roojsbuilder";
30                 }
31
32                 public int get_priority (GtkSource.CompletionContext context)
33                 {
34                   return 200;
35                 }
36                 
37                 public  void activate (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal)
38                 {
39                         GLib.debug("compelte activate");
40                         var  p = (CompletionProposal) proposal;
41                         global::Gtk.TextMark end_mark = null;
42                         global::Gtk.TextIter begin, end;
43
44                         if (!context.get_bounds(out begin, out end)) {
45                                 return;
46                         }  
47                         var buffer = begin.get_buffer();
48                 
49                         var  word = p.label;
50                         var len = -1;
51                         
52
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.
56                          */
57                         if (!end.ends_line() &&
58                                 !end.get_char().isspace() &&
59                                 !end.ends_word ())
60                         {
61                                 var word_end = end;
62
63                                 if (word_end.forward_word_end ()) {
64                                         var text = end.get_slice(word_end);
65                                         if (text.length > word.length) {
66                                                 return;
67                                         }
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); 
72                                         }
73                                 }
74                         }
75
76                         buffer.begin_user_action();
77                         buffer.delete (ref begin, ref end);
78                         buffer.insert ( ref begin, word, len);
79                         buffer.end_user_action ();
80
81                         if (end_mark != null)
82                         {
83                                 buffer.get_iter_at_mark(out end, end_mark);
84                                 buffer.select_range(end,  end);
85                                 buffer.delete_mark(end_mark);
86                         }
87                 
88
89                 }
90
91
92                 public  void display (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal, GtkSource.CompletionCell cell)
93                 {
94                         GLib.debug("compelte display");
95                         var col = cell.get_column();
96                         
97                         var p = (CompletionProposal) proposal;
98                         switch(col) {
99                                 case GtkSource.CompletionColumn.TYPED_TEXT:
100                                         cell.set_text(p.label);
101                                         break;
102                                 case GtkSource.CompletionColumn.ICON:
103                                         cell.set_icon_name("completion-snippet-symbolic");
104                                         break;
105                                 case  GtkSource.CompletionColumn.COMMENT:
106                                         cell.set_text(p.info);
107                                         break;
108                                 case GtkSource.CompletionColumn.DETAILS:
109                                         cell.set_text(p.text);
110                                         break;
111                                 default:
112                                         cell.set_text(null);
113                                         break;
114                         }       
115                 }
116
117                 
118          
119                 internal  async GLib.ListModel populate_async (GtkSource.CompletionContext context, GLib.Cancellable? cancellable)
120                 {
121                         GLib.debug("pupoulate async");
122
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);
127                         } else {
128                                 res = null;
129                         }
130                         this.model = new CompletionModel(this, context, res, cancellable); 
131                         var word = context.get_word();
132                         
133                         
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);
140                         return filter_model; 
141                         
142                          
143                         
144                 }
145
146                 internal  void refilter (GtkSource.CompletionContext context, GLib.ListModel in_model)
147                 {
148  
149                         GLib.debug("pupoulate refilter");
150          
151
152                         var word = context.get_word();
153                         this.filter.set_search(word);
154                  
155                 
156                 }
157
158
159 /*
160                 public bool activate_proposal (GtkSource.CompletionProposal proposal, TextIter iter)
161                 {
162                         var istart = iter;
163                         istart.backward_find_char(is_space, null);
164                         istart.forward_char();
165
166                 //    var search = iter.get_text(istart);           
167                 
168                         var buffer = iter.get_buffer();
169                         buffer.delete(ref istart, ref iter);
170                         buffer.insert(ref istart, proposal.get_text(), -1);
171                 
172                         return true;
173                 }
174   
175          
176
177                 private bool is_space(unichar space){
178                         return space.isspace() || space.to_string() == "";
179                 }
180                 */
181                  
182         }
183         public class CompletionModel : Object, GLib.ListModel 
184         {
185                 CompletionProvider provider;
186                 Gee.ArrayList<CompletionProposal> items;
187                 string search;
188                 int minimum_word_size = 2;
189                 
190                 public Cancellable? cancellable;
191                 
192                 public CompletionModel(CompletionProvider provider, GtkSource.CompletionContext context, Lsp.CompletionList? res, Cancellable? cancellable)
193                 {
194                         this.provider = provider;
195                         this.cancellable = cancellable;
196                         this.items = new Gee.ArrayList<CompletionProposal>();
197                         
198                         var word = context.get_word();
199                         GLib.debug("looking for %s", word);
200                         this.search = word;
201                         if (res != null) {
202                                 foreach(var comp in res.items) {
203                                          
204                                         this.items.add(new CompletionProposal(comp));   
205                                         
206                                 }
207                         }
208                     print("GOT %d results\n", (int) items.size); 
209                         // WHY TWICE?
210                     if (this.items.size < this.minimum_word_size) {
211                                 return;
212                     }
213                 
214                     items.sort((a, b) => {
215                             return ((string)(a.text)).collate((string)(b.text));
216                     });
217                 
218                 }
219                 
220                  
221                 
222                 public GLib.Object? get_item (uint pos)
223                 {
224                         return (Object) this.items.get((int) pos);
225                 }
226                 public GLib.Type  get_item_type ()
227                 {
228                         return typeof(GtkSource.CompletionProposal);
229                 }
230                 public   uint get_n_items () 
231                 {
232                         return this.items.size;
233                 }
234                 public bool can_filter (string word) 
235                 {
236                         if (word == null || word[0] == 0) {
237                                 return false;
238                         }
239  
240                         if (word.length < this.minimum_word_size) {
241                                 return false;
242                         }
243
244                         /* If the new word starts with our initial word, then we can simply
245                          * refilter outside this model using a GtkFilterListModel.
246                          */
247                          
248                          return word.has_prefix(this.search); 
249                 }
250                 public void  cancel ()
251                 {
252                         if (this.cancellable != null) {
253                                 this.cancellable.cancel();
254                         }
255                 }
256
257
258                 
259         }
260         public class CompletionProposal : Object, GtkSource.CompletionProposal 
261         {
262                 
263                 public string label { get; set; default = ""; }
264                 
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)
268                 {
269                         
270                         
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..");
275                 }
276                 
277         }
278
279
280