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