fix line numbering issues with vala generator - hopefully fixes completion in node...
[roobuilder] / src / JsRender / JsRender.vala
1 //<Script type="text/javascript">
2
3 namespace JsRender {
4
5
6         public errordomain Error {
7                 INVALID_FORMAT,
8                 RENAME_FILE_EXISTS
9         }
10                 
11         public abstract class JsRender  : Object {
12                 /**
13                  * @cfg {Array} doubleStringProps list of properties that can be double quoted.
14                  */
15                 public Gee.ArrayList<string> doubleStringProps;
16                 
17                 public string id  = "";
18                 public string name { get; set; default = ""; }   // is the JS name of the file.
19                 public string fullname = "";
20                 public string path = "";  // is the full path to the file.
21                 
22                 public  string relpath {
23                         owned get { 
24                                 return  this.project.path  == this.path ? "" : this.path.substring(this.project.path.length+1);
25                         } 
26                         private set {}
27                 }
28                 public  string reldir {
29                         owned get { 
30                                 return  this.project.path == this.dir ? "" : this. dir.substring(this.project.path.length+1);
31                         } 
32                         private set {}
33                 }
34                 
35                 public  string  dir {
36                         owned get { 
37                                 return GLib.Path.get_dirname(this.path);
38                                  
39                         } 
40                         private set {}
41                 }
42                 
43                 public string file_namespace {
44                         public owned get {
45                                 if (!this.name.contains(".")) {
46                                         return "";
47                                 }
48                                 var bits = this.name.split(".");
49                                 return bits[0];
50                         }
51                         private set {}
52                 }
53                 public string file_without_namespace {
54                         public  owned get {
55                                 if (!this.name.contains(".")) {
56                                         return this.name;
57                                 }
58                                 var bits = this.name.split(".");
59                                 return this.name.substring(bits[0].length +1);
60                         }
61                         private set {}
62                 }
63                 
64                 public string file_ext {
65                         public owned get {
66                                 if (!this.path.contains(".")) {
67                                         return "";
68                                 }
69                                 var bits = this.name.split(".");
70                                 return bits[bits.length-1];
71                         }
72                         private set {}
73                 }
74                 public string parent = "";  // JS parent.
75                 public string region = "";  // RooJS - insert region.
76         
77                 public string title = "";  // a title.. ?? nickname.. ??? -
78
79                 private int _version = 1;   // should we increment this based on the node..?
80                 public int version {
81                         get {
82                                 return ++this._version; // increased on every call? - bit of a kludge until we do real versioning
83                         }
84                         private set {
85                                 this._version = value;
86                         }
87                         
88                 }
89
90                 public string permname;
91                 public string language;
92                 public string content_type;
93                 public string modOrder;
94                 public string xtype;
95                 public uint64 webkit_page_id; // set by webkit view - used to extract extension/etc..
96                 public bool gen_extended  = false; // nodetovala??
97
98                 public Project.Project project;
99
100                 // GTK Specifc
101  
102                 public string build_module; // module to build if we compile (or are running tests...)      
103
104                 //Project : false, // link to container project!
105                 
106                 public Node tree; // the tree of nodes.
107                 
108                 //public GLib.List<JsRender> cn; // child files.. (used by project ... should move code here..)
109
110                 public bool hasParent; 
111                 
112                 public bool loaded;
113                 
114                 public Gee.HashMap<string,string> transStrings; // map of md5 -> string.
115                 public  Gee.HashMap<string,string> namedStrings;
116                 public  Gee.HashMap<string, GLib.ListStore> errorsByType;
117                 
118                 
119
120                 public signal void changed (Node? node, string source); 
121                 
122                  
123                 public signal void compile_notice(string type, string file, int line, string message);
124                 
125
126                 
127                 /**
128                  * UI componenets
129                  * 
130                  */
131                 //public Xcls_Editor editor;
132                 public GLib.ListStore childfiles; // used by directories..
133                 
134                 
135                 //abstract JsRender(Project.Project project, string path); 
136                 
137                 protected JsRender(Project.Project project, string path)
138                 {
139                     
140                         //this.cn = new GLib.List<JsRender>();
141                         GLib.debug("new jsrender %s", path);
142                         this.path = path;
143                         this.project = project;
144                         this.hasParent = false;
145                         this.parent = "";
146                         this.tree = null; 
147                         this.title = "";
148                         this.region = "";
149                         this.permname = "";
150                         this.modOrder = "";
151                         this.language = "";
152                         this.content_type = "";
153                         this.build_module = "";
154                         //this.loaded = false;
155                         //print("JsRender.cto() - reset transStrings\n");
156                         this.transStrings = new Gee.HashMap<string,string>();
157                         this.namedStrings = new Gee.HashMap<string,string>();
158                         // should use basename reallly...
159                         
160                         var ar = this.path.split("/");
161                         // name is in theory filename without .bjs (or .js eventually...)
162                         try {
163                                 Regex regex = new Regex ("\\.(bjs)$");
164
165                                 this.name = ar.length > 0 ? regex.replace(ar[ar.length-1],ar[ar.length-1].length, 0 , "") : "";
166                         } catch (GLib.Error e) {
167                                 this.name = "???";
168                         }
169                         this.fullname = (this.parent.length > 0 ? (this.parent + ".") : "" ) + this.name;
170
171                         this.doubleStringProps = new Gee.ArrayList<string>();
172                         this.childfiles = new GLib.ListStore(typeof(JsRender));
173                         this.errorsByType  = new Gee.HashMap<string, GLib.ListStore>();
174
175                         if (this.relpath == "src/Lsp.vala") {
176                                 GLib.debug("got testing lsp");
177                         }
178
179                 }
180                 
181                 public void renameTo(string name) throws  Error
182                 {
183                         if (this.xtype == "PlainFile") {
184                                 return;
185                         }
186                         var bjs = GLib.Path.get_dirname(this.path) +"/" +  name + ".bjs";
187                         if (FileUtils.test(bjs, FileTest.EXISTS)) {
188                                 throw new Error.RENAME_FILE_EXISTS("File exists %s\n",name);
189                         }
190                         GLib.FileUtils.remove(this.path);
191                         this.removeFiles();
192                         // remove other files?
193                         
194                         this.name = name;
195                         this.path = bjs;
196                         
197                 }
198                 
199
200                 
201                 // not sure why xt is needed... -> project contains xtype..
202                 
203                 public static JsRender factory(string xt, Project.Project project, string path) throws Error
204                 {
205          
206                         switch (xt) {
207                                 case "Gtk":
208                                         return new Gtk(project, path);
209                                         
210                                 case "Roo":
211                                         return new Roo((Project.Roo) project, path);
212 //                      case "Flutter":
213 //                                      return new Flutter(project, path);
214                                 case "PlainFile":
215                                         return new PlainFile(project, path);
216                         }
217                         throw new Error.INVALID_FORMAT("JsRender Factory called with xtype=%s", xt);
218                         //return null;    
219                 }
220                 
221         
222         
223                 public string nickType()
224                 {
225                         var ar = this.name.split(".");
226                         string[] ret = {};
227                         for (var i =0; i < ar.length -1; i++) {
228                                 ret += ar[i];
229                         }
230                         return string.joinv(".", ret);
231                         
232                 }
233                 public string nickName()
234                 {
235                         var ar = this.name.split(".");
236                         return ar[ar.length-1];
237                         
238                 }
239                 
240                 public string nickNameSplit()
241                 {
242                         var n = this.nickName();
243                         var ret = "";
244                         var len = 0;
245                         for (var i = 0; i < n.length; i++) {
246                                 if (i!=0 && n.get(i).isupper() && len > 10) {
247                                         ret +="\n";
248                                         len= 0;
249                                 }
250                                 ret += n.get(i).to_string();
251                                 len++;
252                         }
253                         
254
255                         
256                         return ret;
257                 
258                 }
259                 
260                 Gdk.Pixbuf screenshot = null;
261                 Gdk.Pixbuf screenshot92 = null;
262                 Gdk.Pixbuf screenshot368 = null;
263                 
264                 public Gdk.Pixbuf? getIcon(int size = 0) {
265                     var fname = this.getIconFileName( );                
266                     if (!FileUtils.test(fname, FileTest.EXISTS)) {
267                 GLib.debug("PIXBUF %s:  %s does not exist?", this.name, fname);
268                                 return null;
269                         }
270                         
271                         switch (size) {
272                                 case 0:
273                                         if (this.screenshot == null) {
274                                                 try { 
275                                                         this.screenshot = new Gdk.Pixbuf.from_file(fname);
276                                                 } catch (GLib.Error e) {}
277                                         }
278                                         return this.screenshot;
279                                 
280                                 case 92:
281                                         
282                                         if (this.screenshot == null) {
283                                                 this.getIcon(0);
284                                                 if (this.screenshot == null) {
285                                                         return null;
286                                                 }
287                                         }
288                                         
289                                         this.screenshot92 = this.screenshot.scale_simple(92, (int) (this.screenshot.height * 92.0 /this.screenshot.width * 1.0 )
290                                                 , Gdk.InterpType.NEAREST) ;
291                                     return this.screenshot92;
292                             
293                             case 368:
294                                         if (this.screenshot == null) {
295                                                 this.getIcon(0);
296                                                 if (this.screenshot == null) {
297                                                         return null;
298                                                 }
299                                         }
300                                         
301                                         this.screenshot368 = this.screenshot.scale_simple(368, (int) (this.screenshot.height * 368.0 /this.screenshot.width * 1.0 )
302                                     , Gdk.InterpType.NEAREST) ;
303                                     return this.screenshot368;
304                     }
305                     return null;
306                 }
307                 
308                 public void writeIcon(Gdk.Pixbuf pixbuf) {
309                         
310                         this.screenshot92 = null;
311                         this.screenshot368 = null;
312                         this.screenshot = null;
313                         try {
314                                 GLib.debug("Wirte %s", this.getIconFileName( ));
315                                 pixbuf.save(this.getIconFileName( ),"png");
316                                 this.screenshot = pixbuf;
317                         
318                         } catch (GLib.Error e) {
319                                 GLib.debug("failed to write pixbuf?");
320                         
321                         }
322                                 
323                          
324                         
325                 
326                 }
327                 public void widgetToIcon(global::Gtk.Widget widget) {
328                         
329                         this.screenshot92 = null;
330                         this.screenshot368 = null;
331                         this.screenshot = null;
332                         
333                         try {
334                         
335
336                         var filename = this.getIconFileName();
337                                 
338
339                                  var p = new global::Gtk.WidgetPaintable(widget);
340                                  var s = new global::Gtk.Snapshot();
341                                  GLib.debug("Width %d, Height %d", widget.get_width(), widget.get_height()); 
342                                  p.snapshot(s, widget.get_width(), widget.get_height());
343                                  var n = s.free_to_node();
344                                  var r = new  Gsk.CairoRenderer();
345                                  r.realize(null);
346                                  var t = r.render_texture(n,null);
347                                  GLib.debug("write to %s", filename);
348                                 t.save_to_png(filename);
349                                  r.unrealize();
350                                          
351                         
352                         } catch (GLib.Error e) {
353                                 GLib.debug("failed to write pixbuf?");
354                         
355                         }
356                                 
357                          
358                         
359                 
360                 }
361
362                 
363                 public string getIconFileName( )
364                 {
365                          
366                         var m5 = GLib.Checksum.compute_for_string(GLib.ChecksumType.MD5,this.path); 
367
368                         var dir = GLib.Environment.get_home_dir() + "/.Builder/icons";
369                         try {
370                                 if (!FileUtils.test(dir, FileTest.IS_DIR)) {
371                                          File.new_for_path(dir).make_directory();
372                                 }
373                         } catch (GLib.Error e) {
374                                 // eakk.. what to do here...
375                         }
376                         var fname = dir + "/" + m5 + ".png";
377                         
378                          
379                         return fname;
380                           
381                 }
382                 
383                 public string toJsonString()
384                 {
385                         if (this.xtype == "PlainFile") {
386                                 return "";
387                         }
388                         var node = new Json.Node(Json.NodeType.OBJECT);
389                         node.set_object(this.toJsonObject());                   
390                         var generator = new JsonGen(node);
391                     generator.indent = 1;
392                     generator.pretty = true;
393                     
394                         
395                         return generator.to_data();
396                 }
397                 
398                 public void saveBJS()
399                 {
400                    // if (!this.loaded) {
401                         ///     GLib.debug("saveBJS - skip - not loaded?");
402                 //          return;
403                     //}
404                     if (this.xtype == "PlainFile") {
405                             return;
406                     }
407                    
408                      
409                     
410                     GLib.debug("WRITE :%s\n " , this.path);// + "\n" + JSON.stringify(write));
411                     try {
412                                 this.writeFile(this.path, this.toJsonString());
413                          
414                     } catch(GLib.Error e) {
415                         print("Save failed");
416                     }
417                 }
418                  
419                  
420
421
422                  
423                  
424                  
425                   
426                 public string jsonHasOrEmpty(Json.Object obj, string key) {
427                         return obj.has_member(key) ? 
428                                                 obj.get_string_member(key) : "";
429                 }
430
431                 
432                 public Json.Object toJsonObject ()
433                 {
434                     
435                     
436                         var ret = new Json.Object();
437                         if (this.xtype == "PlainFile") {
438                                 return ret;
439                         }
440                         
441                         //ret.set_string_member("id", this.id); // not relivant..
442                         ret.set_string_member("name", this.name);
443                         
444                         if (this.project.xtype == "Roo") {
445                                 ret.set_string_member("parent", this.parent == null ? "" : this.parent);
446                                 ret.set_string_member("title", this.title == null ? "" : this.title);
447                                 //ret.set_string_member("path", this.path);
448                                 //ret.set_string_member("items", this.items);
449                                 ret.set_string_member("permname", this.permname  == null ? "" : this.permname);
450                                 ret.set_string_member("modOrder", this.modOrder  == null ? "" : this.modOrder);
451                         }
452                         if (this.project.xtype == "Gtk") {
453  
454                                 ret.set_string_member("build_module", this.build_module  );
455                         }
456                         ret.set_boolean_member("gen_extended", this.gen_extended);
457                         
458                         if (this.transStrings.size > 0) {
459                                 var tr =  new Json.Object();
460                                 var iter = this.transStrings.map_iterator();
461                                 while (iter.next()) {
462                                         tr.set_string_member(iter.get_value(), iter.get_key());
463                                 }
464                                 ret.set_object_member("strings", tr);
465             }
466
467             
468             
469                         if (this.namedStrings.size > 0) {
470                                 var tr =  new Json.Object();
471                                 var iter = this.namedStrings.map_iterator();
472                                 while (iter.next()) {
473                                         tr.set_string_member(iter.get_key(), iter.get_value());
474                                 }
475                                 ret.set_object_member("named_strings", tr);
476             }
477                         
478                         var ar = new Json.Array();
479                         // empty files do not have a tree.
480                         if (this.tree != null) {
481                                 ar.add_object_element(this.tree.toJsonObject());
482                         }
483                         ret.set_array_member("items", ar);
484                 
485                     return ret;
486                 }
487                 
488                 
489
490                 public string getTitle ()
491                 {
492                     if (this.title == null) { // not sure why this happens..
493                         return "";
494                 }
495                     if (this.title.length > 0) {
496                         return this.title;
497                     }
498                     var a = this.path.split("/");
499                     return a[a.length-1];
500                 }
501                 public string getTitleTip()
502                 {
503                     if (this.title.length > 0) {
504                         return "<b>" + this.title + "</b> " + this.path;
505                     }
506                     return this.path;
507                 }
508                 /*
509                     sortCn: function()
510                     {
511                         this.cn.sort(function(a,b) {
512                             return a.path > b.path;// ? 1 : -1;
513                         });
514                     },
515                 */
516                     // should be in palete provider really..
517
518
519                 public Palete.Palete palete()
520                 {
521                         // error on plainfile?
522                         return this.project.palete;
523
524                 }
525                 
526                 public string guessName(Node ar) // turns the object into full name.
527                 {
528                      // eg. xns: Roo, xtype: XXX -> Roo.xxx
529                     if (!ar.hasXnsType()) {
530                        return "";
531                     }
532                     
533                     return ar.get("xns") + "." + ar.get("xtype");
534                                       
535                                         
536                 }
537                 /**
538                  *  non-atomic write (replacement for put contents, as it creates temporary files.
539                  */
540                 public void writeFile(string path, string contents) throws GLib.IOError, GLib.Error
541                 {
542
543                                  
544                         var f = GLib.File.new_for_path(path);
545                         var data_out = new GLib.DataOutputStream(
546                                           f.replace(null, false, GLib.FileCreateFlags.NONE, null)
547                        );
548                         data_out.put_string(contents, null);
549                         data_out.close(null);
550                 }
551                  
552                 
553                 
554                 public  Node? lineToNode(int line)
555                 {
556                         if (this.tree == null) {
557                                 return null;
558                         }
559                         return this.tree.lineToNode(line);
560                         
561                         
562                 }
563                 
564                 public GLib.ListStore toListStore()
565                 {
566                         var ret = new GLib.ListStore(typeof(Node));
567                         ret.append(this.tree);
568                         return ret;
569                 }
570                  
571                 
572                 // used to handle list of files in project editor (really Gtk only)
573                 public bool compile_group_selected {
574                         get {
575                                 var gproj = (Project.Gtk) this.project;
576                                 
577                                 if (gproj.active_cg == null) {
578                                         return false;
579                                 }
580                                 if (this.xtype == "Dir") {
581                                         // show ticked if all ticked..
582                                         var ticked = true;
583                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
584                                                 var f = (JsRender) this.childfiles.get_item(i);
585                                                 if (!f.compile_group_selected) {
586                                                         ticked = false;
587                                                         break;
588                                                 }
589                                         }
590                                         return ticked;
591                                 
592                                 
593                                 }
594                                 if (gproj.active_cg.sources == null) {
595                                         GLib.debug("compile_group_selected - sources is null? ");
596                                         return false;
597                                 }
598
599                                 return gproj.active_cg.sources.contains(this.relpath);
600                                 
601                         }
602                         set {
603                                 
604                                 var gproj = (Project.Gtk) this.project;
605                                 
606                                 if (gproj.active_cg == null) {
607                                         return;
608                                 }
609                                 if (gproj.active_cg.loading_ui) {
610                                         return;
611                                 }
612                                 
613                                 if (this.xtype == "Dir") {
614                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
615                                                 var f = (JsRender) this.childfiles.get_item(i);
616                                                 f.compile_group_selected = value;
617  
618                                         }
619                                         return;
620                                  
621                                 }
622                                 
623                                 
624                                 
625                                 if (value == false) {
626                                         GLib.debug("REMOVE %s", this.relpath);
627                                         
628                                         gproj.active_cg.sources.remove(this.relpath);
629                                         return;
630                                 }
631                                 if (!gproj.active_cg.sources.contains(this.relpath)) { 
632                                         GLib.debug("ADD %s", this.relpath);
633                                         gproj.active_cg.sources.add(this.relpath);
634                                 }
635                         
636                         }
637                 }
638                 /*
639                 public bool compile_group_hidden {
640                         get {
641                                 var gproj = (Project.Gtk) this.project;
642                                 
643                                 
644                                 return gproj.hidden.contains(this.relpath);
645                                 
646                         }
647                         set {
648                                 
649                                 var gproj = (Project.Gtk) this.project;
650                                 
651                                 if (gproj.active_cg == null) {
652                                         return;
653                                 }
654                                 if (gproj.active_cg.loading_ui) {
655                                         return;
656                                 } 
657                                 if (value == false) {
658                                         GLib.debug("REMOVE %s", this.relpath);
659                                         
660                                         gproj.hidden.remove(this.relpath);
661                                         return;
662                                 }
663                                 if (!gproj.hidden.contains(this.relpath)) { 
664                                         gproj.hidden.add(this.relpath);
665                                         // hiding a project will auto clear it.
666                                         for(var i = 0; i < this.childfiles.n_items; i++ ) {
667                                                 var f = (JsRender) this.childfiles.get_item(i);
668                                                 f.compile_group_selected = false;
669                                         }
670                                         return;
671                                         
672                                 }
673                         
674                         }
675                 }
676                 */
677                 public void remove()
678                 {
679                         if (this.xtype == "Dir") {
680                                 return;
681                         }
682                         // cleans up build (should really be in children..
683                         this.removeFile(this.path);
684                         if (this.path.has_suffix(".bjs") && this.project.xtype == "Roo") {
685                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".js");
686                                 return;
687                         }
688                         if (this.path.has_suffix(".bjs") && this.project.xtype == "Gtk") {
689                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".vala");
690                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".c");
691                                 this.removeFile(this.path.substring(0, this.path.length-4) + ".o");                             
692                         }
693                         if (this.path.has_suffix(".vala") && this.project.xtype == "Gtk") {
694                                 this.removeFile(this.path.substring(0, this.path.length-5) + ".c");
695                                 this.removeFile(this.path.substring(0, this.path.length-5) + ".o");                             
696                         }       
697                 
698                 
699                 }
700                 
701                 private void removeFile(string path)
702                 {
703
704                         if (GLib.FileUtils.test(path, GLib.FileTest.EXISTS)) {
705                                 GLib.FileUtils.unlink(path);
706                         }
707
708                 }
709                 public string relTargetName()
710                 {
711                         return this.targetName().substring(this.project.path.length +1);
712                 
713                 }
714                 
715                 public string to_url()
716                 {
717                         return File.new_for_path (this.targetName()).get_uri ();
718                 }
719                 public Palete.LanguageClient? getLanguageServer()
720                 {
721                         
722                         return this.project.getLanguageServer(this.language_id());
723                 
724                 }
725                 public GLib.ListStore getErrors(string n)
726                 {
727                         var ls = this.errorsByType.get(n);
728                         if (ls == null) {
729                                 ls = new GLib.ListStore(typeof(Palete.CompileError));
730                                 this.errorsByType.set(n, ls );
731                         }
732                         return ls;
733                 }
734                 
735                 
736                 public abstract string language_id();
737                 public abstract void save();
738                 public abstract void saveHTML(string html);
739                 public abstract string toSource() ;
740                 public abstract string toSourceCode() ; // used by commandline tester..
741                 public abstract void setSource(string str);
742                 public abstract string toSourcePreview() ;
743                 public abstract void removeFiles() ;
744                 public abstract void  findTransStrings(Node? node );
745                 public abstract string toGlade();
746                 public abstract string targetName();
747                 public abstract void loadItems() throws GLib.Error;
748         } 
749
750 }
751  
752