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