1 
2 /******************************************************************************
3 * MODULE     : new_buffer.cpp
4 * DESCRIPTION: Buffer management
5 * COPYRIGHT  : (C) 1999-2012  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "tm_data.hpp"
13 #include "convert.hpp"
14 #include "file.hpp"
15 #include "web_files.hpp"
16 #include "tm_link.hpp"
17 #include "message.hpp"
18 #include "dictionary.hpp"
19 #include "new_document.hpp"
20 #include "merge_sort.hpp"
21 
22 array<tm_buffer> bufs;
23 
24 string propose_title (string old_title, url u, tree doc);
25 
26 /******************************************************************************
27 * Check for changes in the buffer
28 ******************************************************************************/
29 
30 void
attach_notifier()31 tm_buffer_rep::attach_notifier () {
32   if (notify) return;
33   string id= as_string (buf->name, URL_UNIX);
34   tree& st (subtree (the_et, rp));
35   call ("buffer-initialize", id, st, buf->name);
36   lns= link_repository (true);
37   lns->insert_locus (id, st, "buffer-notify");
38   notify= true;
39 }
40 
41 bool
needs_to_be_saved()42 tm_buffer_rep::needs_to_be_saved () {
43   if (buf->read_only) return false;
44   for (int i=0; i<N(vws); i++)
45     if (vws[i]->ed->need_save ())
46       return true;
47   return false;
48 }
49 
50 bool
needs_to_be_autosaved()51 tm_buffer_rep::needs_to_be_autosaved () {
52   if (buf->read_only) return false;
53   for (int i=0; i<N(vws); i++)
54     if (vws[i]->ed->need_save (false))
55       return true;
56   return false;
57 }
58 
59 /******************************************************************************
60 * Manipulation of buffer list
61 ******************************************************************************/
62 
63 void
insert_buffer(url name)64 insert_buffer (url name) {
65   if (is_none (name)) return;
66   if (!is_nil (concrete_buffer (name))) return;
67   tm_buffer buf= tm_new<tm_buffer_rep> (name);
68   bufs << buf;
69 }
70 
71 void
remove_buffer(tm_buffer buf)72 remove_buffer (tm_buffer buf) {
73   int nr, n= N(bufs);
74   for (nr=0; nr<n; nr++)
75     if (bufs[nr] == buf) {
76       for (int i=0; i<N(buf->vws); i++)
77         delete_view (abstract_view (buf->vws[i]));
78       if (n == 1 && number_of_servers () == 0)
79         get_server () -> quit ();
80       for (int i=nr; i<n-1; i++)
81         bufs[i]= bufs[i+1];
82       bufs->resize (n-1);
83       tm_delete (buf);
84       return;
85     }
86 }
87 
88 void
remove_buffer(url name)89 remove_buffer (url name) {
90   tm_buffer buf= concrete_buffer (name);
91   if (!is_nil (buf)) remove_buffer (buf);
92 }
93 
94 int
number_buffers()95 number_buffers () {
96   return N(bufs);
97 }
98 
99 array<url>
get_all_buffers()100 get_all_buffers () {
101   array<url> r;
102   for (int i=N(bufs)-1; i>=0; i--)
103     r << bufs[i]->buf->name;
104   return r;
105 }
106 
107 tm_buffer
concrete_buffer(url name)108 concrete_buffer (url name) {
109   int i, n= N(bufs);
110   for (i=0; i<n; i++)
111     if (bufs[i]->buf->name == name)
112       return bufs[i];
113   return nil_buffer ();
114 }
115 
116 tm_buffer
concrete_buffer_insist(url u)117 concrete_buffer_insist (url u) {
118   tm_buffer buf= concrete_buffer (u);
119   if (!is_nil (buf)) return buf;
120   buffer_load (u);
121   return concrete_buffer (u);
122 }
123 
124 /******************************************************************************
125 * Buffer names
126 ******************************************************************************/
127 
128 url
get_current_buffer()129 get_current_buffer () {
130   tm_view vw= concrete_view (get_current_view ());
131   return vw->buf->buf->name;
132 }
133 
134 url
get_current_buffer_safe()135 get_current_buffer_safe () {
136   url v= get_current_view_safe ();
137   if (is_none (v)) return v;
138   return concrete_view (v)->buf->buf->name;
139 }
140 
141 url
path_to_buffer(path p)142 path_to_buffer (path p) {
143   int i;
144   for (i=0; i<N(bufs); i++)
145     if (bufs[i]->rp <= p)
146       return bufs[i]->buf->name;
147   return url_none ();
148 }
149 
150 void
rename_buffer(url name,url new_name)151 rename_buffer (url name, url new_name) {
152   if (new_name == name || is_nil (concrete_buffer (name))) return;
153   kill_buffer (new_name);
154   tm_buffer buf= concrete_buffer (name);
155   if (is_nil (buf)) return;
156   notify_rename_before (name);
157   buf->buf->name= new_name;
158   buf->buf->master= new_name;
159   array<url> vs= buffer_to_views (new_name);
160   for (int i=0; i<N(vs); i++)
161     view_to_editor (vs[i]) -> notify_change (THE_ENVIRONMENT);
162   notify_rename_after (new_name);
163   tree doc= subtree (the_et, buf->rp);
164   string title= propose_title (buf->buf->title, new_name, doc);
165   set_title_buffer (new_name, title);
166 }
167 
168 url
make_new_buffer()169 make_new_buffer () {
170   int i=1;
171   while (true) {
172     url name= url_scratch ("no_name_", ".tm", i);
173     if (is_nil (concrete_buffer (name))) {
174       set_buffer_tree (name, tree (DOCUMENT));
175       return name;
176     }
177     else i++;
178   }
179 }
180 
181 bool
buffer_has_name(url name)182 buffer_has_name (url name) {
183   return !is_scratch (name);
184 }
185 
186 /******************************************************************************
187 * Buffer title
188 ******************************************************************************/
189 
190 string
propose_title(string old_title,url u,tree doc)191 propose_title (string old_title, url u, tree doc) {
192   string name= as_string (tail (u));
193   if (starts (name, "no_name_") && ends (name, ".tm")) {
194     string no_name= translate ("No name");
195     for (int i=0; i<N(no_name); i++)
196       if (((unsigned char) (no_name[i])) >= (unsigned char) 128)
197 	{ no_name= "No name"; break; }
198     name= no_name * " [" * name (8, N(name) - 3) * "]";
199   }
200   if ((name == "") || (name == "."))
201     name= as_string (tail (u * url_parent ()));
202   if ((name == "") || (name == "."))
203     name= as_string (u);
204   if (is_rooted_tmfs (u))
205     name= as_string (call ("tmfs-title", as_string (u), object (doc)));
206 
207   int i, j;
208   for (j=1; true; j++) {
209     bool flag= true;
210     string ret (name);
211     if (j>1) ret= name * " (" * as_string (j) * ")";
212     if (ret == old_title) return ret;
213     for (i=0; i<N(bufs); i++)
214       if (bufs[i]->buf->title == ret) flag= false;
215     if (flag) return ret;
216   }
217 }
218 
219 string
get_title_buffer(url name)220 get_title_buffer (url name) {
221   tm_buffer buf= concrete_buffer (name);
222   if (is_nil (buf)) return "";
223   return buf->buf->title;
224 }
225 
226 void
set_title_buffer(url name,string title)227 set_title_buffer (url name, string title) {
228   tm_buffer buf= concrete_buffer (name);
229   if (is_nil (buf)) return;
230   if (buf->buf->title == title) return;
231   buf->buf->title= title;
232   array<url> vs= buffer_to_views (name);
233   for (int i=0; i<N(vs); i++) {
234     tm_window win= concrete_window (view_to_window (vs[i]));
235     if (win != NULL) {
236       win->set_window_name (title);
237       win->set_window_url (name);
238     }
239   }
240 }
241 
242 /******************************************************************************
243 * Setting and getting the buffer tree contents
244 ******************************************************************************/
245 
246 void
set_buffer_data(url name,new_data data)247 set_buffer_data (url name, new_data data) {
248   array<url> vs= buffer_to_views (name);
249   for (int i=0; i<N(vs); i++)
250     view_to_editor (vs[i]) -> set_data (data);
251 }
252 
253 void
set_buffer_tree(url name,tree doc)254 set_buffer_tree (url name, tree doc) {
255   tm_buffer buf= concrete_buffer (name);
256   if (is_nil (buf)) {
257     insert_buffer (name);
258     buf= concrete_buffer (name);
259     tree body= detach_data (doc, buf->data);
260     set_document (buf->rp, body);
261     buf->buf->title= propose_title (buf->buf->title, name, body);
262     if (buf->data->project != "") {
263       url prj_name= head (name) * as_string (buf->data->project);
264       buf->prj= concrete_buffer_insist (prj_name);
265     }
266   }
267   else {
268     string old_title= buf->buf->title;
269     string old_project= buf->data->project->label;
270     tree body= detach_data (doc, buf->data);
271     assign (buf->rp, body);
272     set_buffer_data (name, buf->data);
273     buf->buf->title= propose_title (old_title, name, body);
274     if (buf->data->project != "" && buf->data->project != old_project) {
275       url prj_name= head (name) * as_string (buf->data->project);
276       buf->prj= concrete_buffer_insist (prj_name);
277     }
278   }
279   pretend_buffer_saved (name);
280 }
281 
282 tree
get_buffer_tree(url name)283 get_buffer_tree (url name) {
284   tm_buffer buf= concrete_buffer (name);
285   if (is_nil (buf)) return "";
286   tree body= subtree (the_et, buf->rp);
287   return attach_data (body, buf->data, true);
288 }
289 
290 void
set_buffer_body(url name,tree body)291 set_buffer_body (url name, tree body) {
292   tm_buffer buf= concrete_buffer (name);
293   if (is_nil (buf)) {
294     new_data data;
295     set_buffer_tree (name, attach_data (body, data));
296   }
297   else {
298     assign (buf->rp, body);
299     pretend_buffer_saved (name);
300   }
301 }
302 
303 tree
get_buffer_body(url name)304 get_buffer_body (url name) {
305   tm_buffer buf= concrete_buffer (name);
306   if (is_nil (buf)) return "";
307   return subtree (the_et, buf->rp);
308 }
309 
310 /******************************************************************************
311 * Further information attached to buffers
312 ******************************************************************************/
313 
314 url
get_master_buffer(url name)315 get_master_buffer (url name) {
316   tm_buffer buf= concrete_buffer (name);
317   if (is_nil (buf)) return url_none ();
318   return buf->buf->master;
319 }
320 
321 void
set_master_buffer(url name,url master)322 set_master_buffer (url name, url master) {
323   tm_buffer buf= concrete_buffer (name);
324   if (is_nil (buf)) return;
325   if (buf->buf->master == master) return;
326   buf->buf->master= master;
327   array<url> vs= buffer_to_views (name);
328   for (int i=0; i<N(vs); i++)
329     view_to_editor (vs[i]) -> notify_change (THE_ENVIRONMENT);
330 }
331 
332 void
set_last_save_buffer(url name,int t)333 set_last_save_buffer (url name, int t) {
334   tm_buffer buf= concrete_buffer (name);
335   if (!is_nil (buf)) buf->buf->last_save= t;
336   //cout << "Set last save " << name << " -> " << t << "\n";
337 }
338 
339 int
get_last_save_buffer(url name)340 get_last_save_buffer (url name) {
341   tm_buffer buf= concrete_buffer (name);
342   if (is_nil (buf)) {
343     //cout << "Get last save " << name << " -> *\n";
344     return - (int) (((unsigned int) (-1)) >> 1);
345   }
346   //cout << "Get last save " << name << " -> " << buf->buf->last_save << "\n";
347   return (int) buf->buf->last_save;
348 }
349 
350 bool
is_aux_buffer(url name)351 is_aux_buffer (url name) {
352   tm_buffer buf= concrete_buffer (name);
353   if (is_nil (buf)) return false;
354   return buf->buf->master != buf->buf->name;
355 }
356 
357 double
last_visited(url name)358 last_visited (url name) {
359   tm_buffer buf= concrete_buffer (name);
360   if (is_nil (buf)) return (double) texmacs_time ();
361   return (double) buf->buf->last_visit;
362 }
363 
364 bool
buffer_modified(url name)365 buffer_modified (url name) {
366   tm_buffer buf= concrete_buffer (name);
367   if (is_nil (buf)) return false;
368   return buf->needs_to_be_saved ();
369 }
370 
371 bool
buffer_modified_since_autosave(url name)372 buffer_modified_since_autosave (url name) {
373   tm_buffer buf= concrete_buffer (name);
374   if (is_nil (buf)) return false;
375   return buf->needs_to_be_autosaved ();
376 }
377 
378 void
pretend_buffer_modified(url name)379 pretend_buffer_modified (url name) {
380   tm_buffer buf= concrete_buffer (name);
381   if (is_nil (buf)) return;
382   array<url> vs= buffer_to_views (name);
383   for (int i=0; i<N(vs); i++)
384     view_to_editor (vs[i]) -> require_save ();
385 }
386 
387 void
pretend_buffer_saved(url name)388 pretend_buffer_saved (url name) {
389   tm_buffer buf= concrete_buffer (name);
390   if (is_nil (buf)) return;
391   array<url> vs= buffer_to_views (name);
392   for (int i=0; i<N(vs); i++)
393     view_to_editor (vs[i]) -> notify_save ();
394   set_last_save_buffer (name, last_modified (name));
395 }
396 
397 void
pretend_buffer_autosaved(url name)398 pretend_buffer_autosaved (url name) {
399   tm_buffer buf= concrete_buffer (name);
400   if (is_nil (buf)) return;
401   array<url> vs= buffer_to_views (name);
402   for (int i=0; i<N(vs); i++)
403     view_to_editor (vs[i]) -> notify_save (false);
404 }
405 
406 void
attach_buffer_notifier(url name)407 attach_buffer_notifier (url name) {
408   tm_buffer buf= concrete_buffer (name);
409   if (is_nil (buf)) return;
410   buf->attach_notifier ();
411 }
412 
413 /******************************************************************************
414 * Loading
415 ******************************************************************************/
416 
417 tree
attach_subformat(tree t,url u,string fm)418 attach_subformat (tree t, url u, string fm) {
419   if (fm != "verbatim" && fm != "scheme" && fm != "cpp") return t;
420   string s= suffix (u);
421   if (s == "scm") fm= "scheme";
422   if (s == "py")  fm= "python";
423   if (s == "cpp" || s == "hpp" || s == "cc" || s == "hh") fm= "cpp";
424   if (s == "mmx" || s == "mmh") fm= "mathemagix";
425   if (s == "sce" || s == "sci") fm= "scilab";
426   if (fm == "verbatim") return t;
427   hashmap<string,tree> h (UNINIT, extract (t, "initial"));
428   h (MODE)= "prog";
429   h (PROG_LANGUAGE)= fm;
430   return change_doc_attr (t, "initial", make_collection (h));
431 }
432 
433 tree
import_loaded_tree(string s,url u,string fm)434 import_loaded_tree (string s, url u, string fm) {
435   set_file_focus (u);
436   if (fm == "generic" && suffix (u) == "txt") fm= "verbatim";
437   if (fm == "generic") fm= get_format (s, suffix (u));
438   if (fm == "texmacs" && starts (s, "(document (TeXmacs")) fm= "stm";
439   if (fm == "verbatim" && starts (s, "(document (TeXmacs")) fm= "stm";
440   tree t= generic_to_tree (s, fm * "-document");
441   tree links= extract (t, "links");
442   if (N (links) != 0)
443     (void) call ("register-link-locations", object (u), object (links));
444   return attach_subformat (t, u, fm);
445 }
446 
447 tree
import_tree(url u,string fm)448 import_tree (url u, string fm) {
449   u= resolve (u, "fr");
450   set_file_focus (u);
451   string s;
452   if (is_none (u) || load_string (u, s, false)) return "error";
453   return import_loaded_tree (s, u, fm);
454 }
455 
456 bool
buffer_import(url name,url src,string fm)457 buffer_import (url name, url src, string fm) {
458   tree t= import_tree (src, fm);
459   if (t == "error") return true;
460   set_buffer_tree (name, t);
461   return false;
462 }
463 
464 bool
buffer_load(url name)465 buffer_load (url name) {
466   string fm= file_format (name);
467   return buffer_import (name, name, fm);
468 }
469 
470 hashmap<string,tree> style_tree_cache ("");
471 
472 tree
load_style_tree(string package)473 load_style_tree (string package) {
474   if (style_tree_cache->contains (package))
475     return style_tree_cache [package];
476   url name= url_none ();
477   url styp= "$TEXMACS_STYLE_PATH";
478   if (ends (package, ".ts")) name= package;
479   else name= styp * (package * ".ts");
480   name= resolve (name);
481   string doc_s;
482   if (!load_string (name, doc_s, false)) {
483     tree doc= texmacs_document_to_tree (doc_s);
484     if (is_compound (doc)) doc= extract (doc, "body");
485     style_tree_cache (package)= doc;
486     return doc;
487   }
488   style_tree_cache (package)= "";
489   return "";
490 }
491 
492 /******************************************************************************
493 * Saving
494 ******************************************************************************/
495 
496 bool
export_tree(tree doc,url u,string fm)497 export_tree (tree doc, url u, string fm) {
498   // NOTE: hook for encryption
499   tree init= extract (doc, "initial");
500   if (fm == "texmacs")
501     for (int i=0; i<N(init); i++)
502       if (is_func (init[i], ASSOCIATE, 2) && init[i][0] == "encryption")
503 	doc= as_tree (call ("tree-export-encrypted",
504 			    object (u), object (doc)));
505   // END hook
506   if (fm == "generic") fm= "verbatim";
507   string s= tree_to_generic (doc, fm * "-document");
508   if (s == "* error: unknown format *") return true;
509   return save_string (u, s);
510 }
511 
512 bool
buffer_export(url name,url dest,string fm)513 buffer_export (url name, url dest, string fm) {
514   tm_view vw= concrete_view (get_recent_view (name));
515   ASSERT (vw != NULL, "view expected");
516 
517   if (fm == "postscript" || fm == "pdf") {
518     int old_stamp= last_modified (dest, false);
519     vw->ed->print_to_file (dest);
520     int new_stamp= last_modified (dest, false);
521     return new_stamp <= old_stamp;
522   }
523 
524   tree body= subtree (the_et, vw->buf->rp);
525   if (fm == "verbatim")
526     body= vw->ed->exec_verbatim (body);
527   if (fm == "html")
528     body= vw->ed->exec_html (body);
529   //if (fm == "latex")
530   //body= vw->ed->exec_latex (body);
531 
532   vw->ed->get_data (vw->buf->data);
533   tree doc= attach_data (body, vw->buf->data, !vw->ed->get_save_aux());
534 
535   if (fm == "latex")
536     doc= change_doc_attr (doc, "view", as_string (abstract_view (vw)));
537 
538   object arg1 (vw->buf->buf->name);
539   object arg2 (body);
540   tree links= as_tree (call ("get-link-locations", arg1, arg2));
541   if (N (links) != 0)
542     doc << compound ("links", links);
543 
544   return export_tree (doc, dest, fm);
545 }
546 
547 tree
latex_expand(tree doc,url name)548 latex_expand (tree doc, url name) {
549   tm_view vw= concrete_view (get_recent_view (name));
550   tree body= vw->ed->exec_latex (extract (doc, "body"));
551   return change_doc_attr (doc, "body", body);
552 }
553 
554 tree
latex_expand(tree doc)555 latex_expand (tree doc) {
556   tm_view vw= concrete_view (url (as_string (extract (doc, "view"))));
557   tree body= vw->ed->exec_latex (extract (doc, "body"));
558   doc= change_doc_attr (doc, "body", body);
559   return remove_doc_attr (doc, "view");
560 }
561 
562 bool
buffer_save(url name)563 buffer_save (url name) {
564   string fm= file_format (name);
565   if (fm == "generic") fm= "verbatim";
566   bool r= buffer_export (name, name, fm);
567   if (!r) pretend_buffer_saved (name);
568   return r;
569 }
570 
571 /******************************************************************************
572 * Loading inclusions
573 ******************************************************************************/
574 
575 static hashmap<string,tree> document_inclusions ("");
576 
577 void
reset_inclusions()578 reset_inclusions () {
579   document_inclusions = hashmap<string,tree> ("");
580 }
581 
582 void
reset_inclusion(url name)583 reset_inclusion (url name) {
584   string name_s= as_string (name);
585   document_inclusions -> reset (name_s);
586 }
587 
588 tree
load_inclusion(url name)589 load_inclusion (url name) {
590   // url name= relative (base_file_name, file_name);
591   string name_s= as_string (name);
592   if (document_inclusions->contains (name_s))
593     return document_inclusions [name_s];
594   tree doc= extract_document (import_tree (name, "generic"));
595   if (!is_func (doc, ERROR)) document_inclusions (name_s)= doc;
596   return doc;
597 }
598