Fix #7327 - cache top_classes load
[roobuilder] / src / Palete / Roo.vala
1 using Gtk;
2
3 namespace Palete {
4
5         
6 /*      
7         
8         
9     public class Introspect.El : Object
10     {
11         public enum eltype { 
12             NS,
13             CLASS,
14             METHOD,
15             PROP
16         }
17                 
18             
19         public eltype type;
20     }
21
22 */
23     public class Roo : Palete {
24                 
25                 Gee.ArrayList<string> top_classes;
26                 public static Gee.HashMap<string,GirObject>? classes_cache = null;
27                 public static Gee.ArrayList<string>? top_classes_cache = null;
28                 
29         public Roo(Project.Project project)
30         {
31
32
33             
34             aconstruct(project);
35             this.name = "Roo";
36                         this.top_classes =  new Gee.ArrayList<string>();
37                         this.load(); // ? initialize the roodata?
38         }
39
40                 Gee.HashMap<string,GirObject> propsFromJSONArray(string type, Json.Array ar, GirObject cls)
41                 {
42
43                         var ret = new Gee.HashMap<string,GirObject>();
44                         
45                         for (var i =0 ; i < ar.get_length(); i++) {
46                                 var o = ar.get_object_element(i);
47                                 var name = o.get_string_member("name"); 
48                                 var prop = new GirObject(type, name );  
49                                  
50                                 prop.type        = o.get_string_member("type");
51                                 prop.doctxt  = o.get_string_member("desc");
52                                 prop.propertyof = o.has_member("memberOf") ? o.get_string_member("memberOf") : "";
53                                 if (prop.propertyof.length < 1)  {
54                                         prop.propertyof = cls.name;
55                                 }
56                                 
57                                 // this is the function default.
58                                 prop.sig = o.has_member("sig") ? o.get_string_member("sig") : "";
59                                 
60                                 if (o.has_member("optvals")  ) {
61                                         var oar = o.get_array_member("optvals");
62                                         
63                                         for (var oi = 0; oi < oar.get_length(); oi++) {
64                                                 prop.optvalues.add(oar.get_string_element(oi));
65                                         }
66                                         
67                                 }       
68                                 
69                                 //print(type + ":" + name +"\n");
70                                 ret.set(name,prop);
71                         }
72                         return ret;
73                 }
74                 
75                 
76                 public override void  load () {
77
78                         if (this.classes != null) {
79                                 return;
80                         }
81                         if (Roo.classes_cache != null) {
82                                 this.classes = Roo.classes_cache;
83                                 this.top_classes = Roo.top_classes_cache ;
84                                 return;
85                         }
86                         
87                         
88                         // this.loadUsageFile(BuilderApplication.configDirectory() + "/resources/RooUsage.txt");
89                         this.classes = new Gee.HashMap<string,GirObject>();
90                         var add_to =  new Gee.HashMap<string,Gee.ArrayList<string>>();
91                                 
92                         var pa = new Json.Parser();
93                         pa.load_from_file(BuilderApplication.configDirectory() + "/resources/roodata.json");
94                         var node = pa.get_root();
95
96                         var clist =  node.get_object(); /// was in data... .get_object_member("data");
97                         clist.foreach_member((o , key, value) => {
98                                 //print("cls:" + key+"\n");
99                          
100                                 var cls = new GirObject("class", key);  
101                                 cls.props = this.propsFromJSONArray("prop", value.get_object().get_array_member("props"),cls);
102                                 cls.signals = this.propsFromJSONArray("signal", value.get_object().get_array_member("events"),cls);
103                                 
104                                 
105                                 if (value.get_object().has_member("methods")) {
106                                         cls.methods = this.propsFromJSONArray("method", value.get_object().get_array_member("methods"),cls);
107                                 }
108                                 if (value.get_object().has_member("implementations")) {
109                                         var vcn = value.get_object().get_array_member("implementations");
110                                         for (var i =0 ; i < vcn.get_length(); i++) {
111                                                 cls.implementations.add(vcn.get_string_element(i));
112                                                 //break; << why!?!
113                                         }                               
114                                 }
115                                 // tree children = 
116                                 
117                                 if (value.get_object().has_member("tree_children")) {
118                                         var vcn = value.get_object().get_array_member("tree_children");                         
119                                         for (var i =0 ; i < vcn.get_length(); i++) {
120                                                 var ad_c = vcn.get_string_element(i);
121                                                 if (!cls.valid_cn.contains(ad_c)) {
122                                                         cls.valid_cn.add( ad_c );
123                                                 }
124                                                 if (!add_to.has_key(ad_c)) {
125                                                         add_to.set(ad_c, new Gee.ArrayList<string>());
126                                                 }
127                                                 if (!add_to.get(ad_c).contains(cls.name)) {
128                                                         add_to.get(ad_c).add(cls.name);
129                                                 }
130                                         }
131                                 }
132                                 
133                                 
134                                 
135                                 
136                                 // tree parent
137                                 
138                                 if (value.get_object().has_member("tree_parent")) {
139                                         var vcn = value.get_object().get_array_member("tree_parent");
140                                         for (var i =0 ; i < vcn.get_length(); i++) {
141                                                 if ("builder" == vcn.get_string_element(i)) {
142                                                         // this class can be added to the top level.
143                                                         GLib.debug("Add %s to *top", cls.name);
144                                                         
145                                                         this.top_classes.add(cls.name);
146                                                         break;
147                                                 }
148                                                 
149                                         }
150                                 }
151  
152                                 this.classes.set(key, cls);
153                         });
154                         
155                         // look for properties of classes, that are atually clasess
156                         // eg. Roo.data.Store has proxy and reader..
157                         
158                         
159                         foreach(var cls in this.classes.values) {
160                                 foreach(var gir_obj in cls.props.values) {
161                                         var types = gir_obj.type.split("|");
162                                         for(var i =0; i < types.length; i++) {
163                                                 var type = types[i];
164                                         
165                                                 if (/^Roo\./.match(type) && classes.has_key(type)) {
166                                                         
167                                                          
168                                                         cls.valid_cn.add(type + ":" +   gir_obj.name );
169                                                         // Roo.bootstrap.panel.Content:east
170                                                         // also means that  Roo.bootstrap.panel.Grid:east works
171                                                         var prop_type = classes.get(type);
172                                                         foreach(var imp_str in prop_type.implementations) {
173                                                                 //GLib.debug("addChild for %s - child=  %s:%s", cls.name, imp_str, gir_obj.name);
174                                                                 cls.valid_cn.add(imp_str + ":" +    gir_obj.name);
175                                                                 if (!add_to.has_key(imp_str)) {
176                                                                         add_to.set( imp_str, new Gee.ArrayList<string>());
177                                                                 }
178                                                                 if (!add_to.get( imp_str).contains(cls.name)) {
179                                                                         add_to.get( imp_str ).add(cls.name );
180                                                                 }
181                                                                 
182                                                         }
183                                                         
184                                                         
185                                                         if (!add_to.has_key( type)) {
186                                                                 add_to.set( type, new Gee.ArrayList<string>());
187                                                         }
188                                                         if (!add_to.get(type).contains(cls.name)) {
189                                                                 add_to.get( type ).add(cls.name );
190                                                         }
191                                                 }
192                                         }
193                                 }
194                                  
195                         }
196                         foreach(var cls in this.classes.values) {
197                                 if (add_to.has_key(cls.name)) {
198                                         cls.can_drop_onto = add_to.get(cls.name);
199                                 }
200                         }
201                         Roo.classes_cache = this.classes;
202                         Roo.top_classes_cache  = this.top_classes;
203                 }
204                   
205                         
206                 public string doc(string what) {
207                         return "";
208                         /*var ns = what.split(".")[0];
209
210
211                         
212                         
213                                 var gir =  Gir.factory(ns);
214                                 return   gir.doc(what);
215                                 */
216                                 
217                         //return typeof(this.comments[ns][what]) == 'undefined' ?  '' : this.comments[ns][what];
218                 }
219
220                 // does not handle implements...
221                 public override GirObject? getClass(string ename)
222                 {
223                         this.load();
224                         return this.classes.get(ename);
225                         
226                 }
227                 
228                 public override Gee.HashMap<string,GirObject> getPropertiesFor(string ename, JsRender.NodePropType ptype)
229                 {
230                         //print("Loading for " + ename);
231                         
232
233                         this.load();
234                                         // if (typeof(this.proplist[ename]) != 'undefined') {
235                                         //print("using cache");
236                                  //   return this.proplist[ename][type];
237                                 //}
238                                 // use introspection to get lists..
239                  
240                         
241                         var cls = this.classes.get(ename);
242                         var ret = new Gee.HashMap<string,GirObject>();
243                         if (cls == null) {
244                                 print("could not find class: %s\n", ename);
245                                 return ret;
246                                 //throw new Error.INVALID_VALUE( "Could not find class: " + ename);
247                 
248                         }
249
250                         //cls.parseProps();
251                         //cls.parseSignals(); // ?? needed for add handler..
252                         //cls.parseMethods(); // ?? needed for ??..
253                         //cls.parseConstructors(); // ?? needed for ??..
254
255                         //cls.overlayParent();
256
257                         switch  (ptype) {
258                                 
259                                 
260                                 case JsRender.NodePropType.PROP:
261                                         return  this.filterProps(cls.props);
262                                 case JsRender.NodePropType.LISTENER:
263                                         return cls.signals;
264                                 case JsRender.NodePropType.METHOD:
265                                         return ret;
266                                 case JsRender.NodePropType.CTOR:
267                                         return ret;
268                                 default:
269                                         throw new Error.INVALID_VALUE( "getPropertiesFor called with: " + ptype.to_string());
270                                         //var ret = new Gee.HashMap<string,GirObject>();
271                                         //return ret;
272                         
273                         }
274                 
275         
276                 //cls.overlayInterfaces(gir);
277
278
279                          
280                 }
281                 
282                 
283                 public Gee.HashMap<string,GirObject>  filterProps(Gee.HashMap<string,GirObject> props)
284                 {
285                         // we shold probably cache this??
286                         
287                         var outprops = new Gee.HashMap<string,GirObject>(); 
288                         
289                         foreach(var k in props.keys) {
290                                 var val = props.get(k);
291                                 
292                                 // special props..
293                                 switch(k) {
294                                         case "listeners" : 
295                                                 continue;
296                                         default:
297                                                 break;
298                                 }
299                                 
300                                  
301                                 if (!val.type.contains(".")) {
302                                         outprops.set(k,val);
303                                         continue;
304                                 }
305                                 
306                                 
307                                  
308                                 // do nothing? - classes not allowed?
309                                 
310                         }
311                         
312                         
313                         return outprops;
314                 
315                 
316                 }
317                 
318                 
319                 public string[] getInheritsFor(string ename)
320                 {
321                         string[] ret = {};
322                         var es = ename.split(".");
323                         var gir = Gir.factory(null, es[0]);
324                         
325                         var cls = gir.classes.get(es[1]);
326                         if (cls == null) {
327                                 return ret;
328                         }
329                         return cls.inheritsToStringArray();
330                         
331
332                 }
333
334
335                 public override void on_child_added(JsRender.Node? parent,JsRender.Node child)
336                 {   
337
338                          return;
339                 }
340                 /*
341                  *  Pulldown options for type
342                  */
343                 public override bool typeOptions(string fqn, string key, string type, out string[] opts) 
344                 {
345                         opts = {};
346                         print("get typeOptions %s (%s)%s", fqn, type, key);
347                         if (type.up() == "BOOL" || type.up() == "BOOLEAN") {
348                                 opts = { "true", "false" };
349                                 return true;
350                          }
351                          
352                          var props = this.getPropertiesFor(fqn, JsRender.NodePropType.PROP);
353                          if (!props.has_key(key)) {
354                                  print("prop %s does not have key %s\n", fqn, key);
355                                  return false;
356                          }
357                          var pr = props.get(key);
358                          if (pr.optvalues.size < 1) {
359                                  print("prop %s no optvalues for %s\n", fqn, key);
360                                  return false;
361                          }
362                          string[] ret = {};
363                          for(var i = 0; i < pr.optvalues.size; i++) {
364                                  ret += pr.optvalues.get(i);
365                          }
366                          opts = ret;
367                          print("prop %s returning optvalues for %s\n", fqn, key);
368                          return true;
369                          
370                 }
371                 public override  List<SourceCompletionItem> suggestComplete(
372                                 JsRender.JsRender file,
373                                 JsRender.Node? node,
374                                 JsRender.NodeProp? xxprop,
375                                 string complete_string
376                 ) { 
377                         
378                         var ret =  new List<SourceCompletionItem>();
379                         // completion rules??
380                         
381                         // Roo......
382                         
383                         // this. (based on the node type)
384                         // this.xxx // Node and any determination...
385                         
386                         if (complete_string.index_of(".",0) < 0) {
387                                 // string does not have a '.'
388                                 // offer up this / Roo / javascript keywords... / look for var string = .. in the code..
389                                 for(var i = 0; i <  JsRender.Lang.match_strings.size ; i++) {
390                                         var str = JsRender.Lang.match_strings.get(i);
391                                         if (complete_string != str && str.index_of(complete_string,0) == 0 ) { // should we ignore exact matches... ???
392                                                 ret.append(new SourceCompletionItem (str, str, null, "javascript : " + str));
393                                         }
394                                         
395                                         
396                                 }
397                                 if (complete_string != "Roo" && "Roo".index_of(complete_string,0) == 0 ) { // should we ignore exact matches... ???
398                                         ret.append(new SourceCompletionItem ("Roo - A Roo class", "Roo", null, "Roo library"));
399                                 }
400                                 if (complete_string != "_this" && "_this".index_of(complete_string,0) == 0 ) { // should we ignore exact matches... ???
401                                         ret.append(new SourceCompletionItem ("_this - the top level element", "_this", null, "Top level element"));
402                                 }
403                                 return ret;
404                         }
405                         // got at least one ".".
406                         var parts = complete_string.split(".");
407                         var curtype = "";
408                         var cur_instance = false;
409                         if (parts[0] == "this") {
410                                 // work out from the node, what the type is...
411                                 if (node == null) {
412                                         print("node is empty - no return\n");
413                                         return ret; // no idea..
414                                 }
415                                 curtype = node.fqn();
416                                 cur_instance = true;
417                         }
418                         if (parts[0] == "Roo") {        
419                                 curtype = "Roo";
420                                 cur_instance = false;
421                         }
422                         
423                         var prevbits = parts[0] + ".";
424                         for(var i =1; i < parts.length; i++) {
425                                 print("matching %d/%d\n", i, parts.length);
426                                 var is_last = i == parts.length -1;
427                                 
428                                 // look up all the properties of the type...
429                                 var cls = this.getClass(curtype);
430                                 if (cls == null) {
431                                         print("could not get class of curtype %s\n", curtype);
432                                         return ret;
433                                 }
434
435                                 if (!is_last) {
436                                 
437                                         // only exact matches from here on...
438                                         if (cur_instance) {
439                                                 if (cls.props.has_key(parts[i])) {
440                                                         var prop = cls.props.get(parts[i]);
441                                                         if (prop.type.index_of(".",0) > -1) {
442                                                                 // type is another roo object..
443                                                                 curtype = prop.type;
444                                                                 prevbits += parts[i] + ".";
445                                                                 continue;
446                                                         }
447                                                         return ret;
448                                                 }
449                                                 
450                                                 
451                                                 
452                                                 // check methods?? - we do not export that at present..
453                                                 return ret;      //no idea...
454                                         }
455                                 
456                                         // not a instance..
457                                         //look for child classes.
458                                         var citer = this.classes.map_iterator();
459                                         var foundit = false;
460                                         while (citer.next()) {
461                                                 var scls = citer.get_key();
462                                                 var look = prevbits + parts[i];
463                                                 if (scls.index_of(look,0) != 0) {
464                                                         continue;
465                                                 }
466                                                 // got a starting match..
467                                                 curtype = look;
468                                                 cur_instance = false;
469                                                 foundit =true;
470                                                 break;
471                                         }
472                                         if (!foundit) {
473                                                 return ret;
474                                         }
475                                         prevbits += parts[i] + ".";
476                                         continue;
477                                 }
478                                 // got to the last element..
479                                 print("Got last element\n");
480                                 if (curtype == "") { // should not happen.. we would have returned already..
481                                         return ret;
482                                 }
483                                 print("Got last element type %s\n",curtype);
484                                 if (!cur_instance) {
485                                         print("matching instance");
486                                         // it's a static reference..
487                                         var citer = this.classes.map_iterator();
488                                         while (citer.next()) {
489                                                 var scls = citer.get_key();
490                                                 var look = prevbits + parts[i];
491                                                 if (parts[i].length > 0 && scls.index_of(look,0) != 0) {
492                                                         continue;
493                                                 }
494                                                 // got a starting match..
495                                                 ret.append(new SourceCompletionItem (
496                                                         scls,
497                                                         scls, 
498                                                         null, 
499                                                         scls));
500                                         }
501                                         return ret;
502                                 }
503                                 print("matching property");
504                                 
505                                 
506                                 
507                                 var citer = cls.methods.map_iterator();
508                                 while (citer.next()) {
509                                         var prop = citer.get_value();
510                                         // does the name start with ...
511                                         if (parts[i].length > 0 && prop.name.index_of(parts[i],0) != 0) {
512                                                 continue;
513                                         }
514                                         // got a matching property...
515                                         // return type?
516                                         ret.append(new SourceCompletionItem (
517                                                          prop.name + prop.sig + " :  ("+ prop.propertyof + ")", 
518                                                         prevbits + prop.name + "(", 
519                                                         null, 
520                                                         prop.doctxt));
521                                 }
522                                 
523                                 // get the properties / methods and subclasses.. of cls..
524                                 // we have cls.. - see if the string matches any of the properties..
525                                 citer = cls.props.map_iterator();
526                                 while (citer.next()) {
527                                         var prop = citer.get_value();
528                                         // does the name start with ...
529                                         if (parts[i].length > 0 && prop.name.index_of(parts[i],0) != 0) {
530                                                 continue;
531                                         }
532                                         // got a matching property...
533                                         
534                                         ret.append(new SourceCompletionItem (
535                                                          prop.name + " : " + prop.type + " ("+ prop.propertyof + ")", 
536                                                         prevbits + prop.name, 
537                                                         null, 
538                                                         prop.doctxt));
539                                 }
540                                          
541                                         
542                                 return ret;     
543                                         
544                                         
545                                 
546                                         
547                                 
548                         }
549                         
550                          
551                         
552                         
553                         
554                         
555                         return ret;
556                 }
557                 public override string[] getChildList(string in_rval)
558         {
559                 if (this.top_classes.size < 1) {
560                         this.load();
561                 }
562                 
563                 
564                 string[] ret = {};
565                 var ar = this.top_classes;
566                 if (in_rval != "*top") {
567                         if (this.classes.has_key(in_rval)) {
568                            // some of these children will be eg: Roo.bootstrap.layout.Region:center
569                                 ar = this.classes.get(in_rval).valid_cn;
570                         } else {
571                                 ar = new Gee.ArrayList<string>();
572                         }
573                 }
574                 
575                 foreach(var str in ar) {
576                         ret += str;
577                 } 
578                 GLib.debug("getChildList for %s returns %s", in_rval, string.joinv(", ", ret));
579                 return ret;     
580                 
581                 //return this.original_getChildList(  in_rval);
582         }
583                 public override string[] getDropList(string rval)
584                 {
585                         // we might be dragging  Roo.bootstrap.layout.Region:center
586                         // in which case we need to lookup Roo.bootstrap.layout.Region
587                         // and see if it's has can_drop_onto
588                         string[] ret = {};
589                         var cls = this.classes.get(rval);
590                         // cls can be null.
591                         if (cls == null && rval.contains(":")) {
592                                 var rr = rval.substring(0,rval.index_of(":"));
593                                 GLib.debug("Converted classname to %s", rr);
594                                 cls = this.classes.get(rr);
595                     }
596                         if (cls == null) {
597                                 return ret; //nothing..
598                         }
599                         
600                         foreach(var str in cls.can_drop_onto) {
601
602                                 ret += str;
603                         }
604                         GLib.debug("getDropList for %s return[] %s", rval, string.joinv(", ", ret));
605                         return ret;
606                                 
607                         
608                         
609                         //return this.default_getDropList(rval);
610                 }       
611     }
612 }
613