]> git.lyx.org Git - lyx.git/blob - src/Lexer.cpp
#9130 Text in main work area isn't rendered with high resolution
[lyx.git] / src / Lexer.cpp
1 /**
2  * \file Lexer.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "Lexer.h"
17 #include "Format.h"
18
19 #include "support/convert.h"
20 #include "support/debug.h"
21 #include "support/FileName.h"
22 #include "support/filetools.h"
23 #include "support/gzstream.h"
24 #include "support/lassert.h"
25 #include "support/lstrings.h"
26 #include "support/lyxalgo.h"
27 #include "support/types.h"
28
29 #include <functional>
30 #include <istream>
31 #include <stack>
32 #include <vector>
33
34 using namespace std;
35 using namespace lyx::support;
36
37 namespace lyx {
38
39 //////////////////////////////////////////////////////////////////////
40 //
41 // Lexer::Pimpl
42 //
43 //////////////////////////////////////////////////////////////////////
44
45
46 ///
47 class Lexer::Pimpl {
48 public:
49         ///
50         Pimpl(LexerKeyword * tab, int num);
51         ///
52         string const getString() const;
53         ///
54         docstring const getDocString() const;
55         ///
56         void printError(string const & message) const;
57         ///
58         void printTable(ostream & os);
59         ///
60         void pushTable(LexerKeyword * tab, int num);
61         ///
62         void popTable();
63         ///
64         bool setFile(FileName const & filename);
65         ///
66         void setStream(istream & i);
67         ///
68         void setCommentChar(char c);
69         ///
70         bool next(bool esc = false);
71         ///
72         int searchKeyword(char const * const tag) const;
73         ///
74         int lex();
75         ///
76         bool eatLine();
77         ///
78         bool nextToken();
79         /// test if there is a pushed token or the stream is ok
80         bool inputAvailable();
81         ///
82         void pushToken(string const &);
83         /// fb_ is only used to open files, the stream is accessed through is.
84         filebuf fb_;
85
86         /// gz_ is only used to open files, the stream is accessed through is.
87         gz::gzstreambuf gz_;
88
89         /// the stream that we use.
90         istream is;
91         ///
92         string name;
93         ///
94         LexerKeyword * table;
95         ///
96         int no_items;
97         ///
98         string buff;
99         ///
100         int status;
101         ///
102         int lineno;
103         ///
104         string pushTok;
105         ///
106         char commentChar;
107         /// used for error messages
108         string context;
109 private:
110         /// non-copyable
111         Pimpl(Pimpl const &);
112         void operator=(Pimpl const &);
113
114         ///
115         void verifyTable();
116         ///
117         class PushedTable {
118         public:
119                 ///
120                 PushedTable()
121                         : table_elem(0), table_siz(0) {}
122                 ///
123                 PushedTable(LexerKeyword * ki, int siz)
124                         : table_elem(ki), table_siz(siz) {}
125                 ///
126                 LexerKeyword * table_elem;
127                 ///
128                 int table_siz;
129         };
130         ///
131         stack<PushedTable> pushed;
132 };
133
134
135
136 namespace {
137
138 class CompareTags
139         : public binary_function<LexerKeyword, LexerKeyword, bool> {
140 public:
141         // used by lower_bound, sort and sorted
142         bool operator()(LexerKeyword const & a, LexerKeyword const & b) const
143         {
144                 // we use the ascii version, because in turkish, 'i'
145                 // is not the lowercase version of 'I', and thus
146                 // turkish locale breaks parsing of tags.
147                 return compare_ascii_no_case(a.tag, b.tag) < 0;
148         }
149 };
150
151 } // end of anon namespace
152
153
154 Lexer::Pimpl::Pimpl(LexerKeyword * tab, int num)
155         : is(&fb_), table(tab), no_items(num),
156           status(0), lineno(0), commentChar('#')
157 {
158         verifyTable();
159 }
160
161
162 string const Lexer::Pimpl::getString() const
163 {
164         return buff;
165 }
166
167
168 docstring const Lexer::Pimpl::getDocString() const
169 {
170         return from_utf8(buff);
171 }
172
173
174 void Lexer::Pimpl::printError(string const & message) const
175 {
176         string const tmpmsg = subst(message, "$$Token", getString());
177         lyxerr << "LyX: " << tmpmsg << " [around line " << lineno
178                 << " of file " << to_utf8(makeDisplayPath(name))
179                 << " current token: '" << getString() << "'"
180                 << " context: '" << context << "']" << endl;
181 }
182
183
184 void Lexer::Pimpl::printTable(ostream & os)
185 {
186         os << "\nNumber of tags: " << no_items << endl;
187         for (int i= 0; i < no_items; ++i)
188                 os << "table[" << i
189                    << "]:  tag: `" << table[i].tag
190                    << "'  code:" << table[i].code << '\n';
191         os.flush();
192 }
193
194
195 void Lexer::Pimpl::verifyTable()
196 {
197         // Check if the table is sorted and if not, sort it.
198         if (table
199             && !lyx::sorted(table, table + no_items, CompareTags())) {
200                 lyxerr << "The table passed to Lexer is not sorted!\n"
201                        << "Tell the developers to fix it!" << endl;
202                 // We sort it anyway to avoid problems.
203                 lyxerr << "\nUnsorted:" << endl;
204                 printTable(lyxerr);
205
206                 sort(table, table + no_items, CompareTags());
207                 lyxerr << "\nSorted:" << endl;
208                 printTable(lyxerr);
209         }
210 }
211
212
213 void Lexer::Pimpl::pushTable(LexerKeyword * tab, int num)
214 {
215         PushedTable tmppu(table, no_items);
216         pushed.push(tmppu);
217
218         table = tab;
219         no_items = num;
220
221         verifyTable();
222 }
223
224
225 void Lexer::Pimpl::popTable()
226 {
227         if (pushed.empty()) {
228                 lyxerr << "Lexer error: nothing to pop!" << endl;
229                 return;
230         }
231
232         PushedTable tmp = pushed.top();
233         pushed.pop();
234         table = tmp.table_elem;
235         no_items = tmp.table_siz;
236 }
237
238
239 bool Lexer::Pimpl::setFile(FileName const & filename)
240 {
241         // Check the format of the file.
242         if (formats.isZippedFile(filename)) {
243                 LYXERR(Debug::LYXLEX, "lyxlex: compressed");
244                 // The check only outputs a debug message, because it triggers
245                 // a bug in compaq cxx 6.2, where is_open() returns 'true' for
246                 // a fresh new filebuf.  (JMarc)
247                 if (gz_.is_open() || istream::off_type(is.tellg()) > -1)
248                         LYXERR(Debug::LYXLEX, "Error in LyXLex::setFile: "
249                                 "file or stream already set.");
250                 gz_.open(filename.toFilesystemEncoding().c_str(), ios::in);
251                 is.rdbuf(&gz_);
252                 name = filename.absFileName();
253                 lineno = 0;
254                 if (!gz_.is_open() || !is.good())
255                         return false;
256         } else {
257                 LYXERR(Debug::LYXLEX, "lyxlex: UNcompressed");
258
259                 // The check only outputs a debug message, because it triggers
260                 // a bug in compaq cxx 6.2, where is_open() returns 'true' for
261                 // a fresh new filebuf.  (JMarc)
262                 if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
263                         LYXERR(Debug::LYXLEX, "Error in Lexer::setFile: "
264                                 "file or stream already set.");
265                 }
266                 fb_.open(filename.toSafeFilesystemEncoding().c_str(), ios::in);
267                 is.rdbuf(&fb_);
268                 name = filename.absFileName();
269                 lineno = 0;
270                 if (!fb_.is_open() || !is.good())
271                         return false;
272         }
273
274         // Skip byte order mark.
275         if (is.peek() == 0xef) {
276                 is.get();
277                 if (is.peek() == 0xbb) {
278                         is.get();
279                         LASSERT(is.get() == 0xbf, /**/);
280                 } else
281                         is.unget();
282         }
283
284         return true;
285 }
286
287
288 void Lexer::Pimpl::setStream(istream & i)
289 {
290         if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
291                 LYXERR(Debug::LYXLEX, "Error in Lexer::setStream: "
292                         "file or stream already set.");
293         }
294         is.rdbuf(i.rdbuf());
295         lineno = 0;
296 }
297
298
299 void Lexer::Pimpl::setCommentChar(char c)
300 {
301         commentChar = c;
302 }
303
304
305 bool Lexer::Pimpl::next(bool esc /* = false */)
306 {
307         if (!pushTok.empty()) {
308                 // There can have been a whole line pushed so
309                 // we extract the first word and leaves the rest
310                 // in pushTok. (Lgb)
311                 if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
312                         buff.clear();
313                         pushTok = split(pushTok, buff, ' ');
314                 } else {
315                         buff = pushTok;
316                         pushTok.clear();
317                 }
318                 status = LEX_TOKEN;
319                 return true;
320         }
321
322
323         unsigned char c = 0; // getc() returns an int
324         char cc = 0;
325         status = 0;
326         while (is && !status) {
327                 is.get(cc);
328                 c = cc;
329
330                 if (c == commentChar) {
331                         // Read rest of line (fast :-)
332 #if 1
333                         // That is not fast... (Lgb)
334                         string dummy;
335                         getline(is, dummy);
336
337                         LYXERR(Debug::LYXLEX, "Comment read: `" << c << dummy << '\'');
338 #else
339                         // unfortunately ignore is buggy (Lgb)
340                         is.ignore(100, '\n');
341 #endif
342                         ++lineno;
343                         continue;
344                 }
345
346                 if (c == '\"') {
347                         buff.clear();
348
349                         if (esc) {
350
351                                 bool escaped = false;
352                                 do {
353                                         escaped = false;
354                                         is.get(cc);
355                                         c = cc;
356                                         if (c == '\r') continue;
357                                         if (c == '\\') {
358                                                 // escape the next char
359                                                 is.get(cc);
360                                                 c = cc;
361                                                 if (c == '\"' || c == '\\')
362                                                         escaped = true;
363                                                 else
364                                                         buff.push_back('\\');
365                                         }
366                                         buff.push_back(c);
367
368                                         if (!escaped && c == '\"')
369                                                 break;
370                                 } while (c != '\n' && is);
371
372                         } else {
373
374                                 do {
375                                         is.get(cc);
376                                         c = cc;
377                                         if (c != '\r')
378                                                 buff.push_back(c);
379                                 } while (c != '\"' && c != '\n' && is);
380
381                         }
382
383                         if (c != '\"') {
384                                 printError("Missing quote");
385                                 if (c == '\n')
386                                         ++lineno;
387                         }
388
389                         buff.resize(buff.size() - 1);
390                         status = LEX_DATA;
391                         break;
392                 }
393
394                 if (c == ',')
395                         continue;              /* Skip ','s */
396
397                 // using relational operators with chars other
398                 // than == and != is not safe. And if it is done
399                 // the type _have_ to be unsigned. It usually a
400                 // lot better to use the functions from cctype
401                 if (c > ' ' && is)  {
402                         buff.clear();
403
404                         do {
405                                 if (esc && c == '\\') {
406                                         // escape the next char
407                                         is.get(cc);
408                                         c = cc;
409                                         //escaped = true;
410                                 }
411                                 buff.push_back(c);
412                                 is.get(cc);
413                                 c = cc;
414                         } while (c > ' ' && c != ',' && is);
415                         status = LEX_TOKEN;
416                 }
417
418                 if (c == '\r' && is) {
419                         // The Windows support has lead to the
420                         // possibility of "\r\n" at the end of
421                         // a line.  This will stop LyX choking
422                         // when it expected to find a '\n'
423                         is.get(cc);
424                         c = cc;
425                 }
426
427                 if (c == '\n')
428                         ++lineno;
429
430         }
431         if (status)
432                 return true;
433
434         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
435         buff.clear();
436         return false;
437 }
438
439
440 int Lexer::Pimpl::searchKeyword(char const * const tag) const
441 {
442         LexerKeyword search_tag = { tag, 0 };
443         LexerKeyword * res =
444                 lower_bound(table, table + no_items,
445                             search_tag, CompareTags());
446         // use the compare_ascii_no_case instead of compare_no_case,
447         // because in turkish, 'i' is not the lowercase version of 'I',
448         // and thus turkish locale breaks parsing of tags.
449         if (res != table + no_items
450             && !compare_ascii_no_case(res->tag, tag))
451                 return res->code;
452         return LEX_UNDEF;
453 }
454
455
456 int Lexer::Pimpl::lex()
457 {
458         //NOTE: possible bug.
459         if (next() && status == LEX_TOKEN)
460                 return searchKeyword(getString().c_str());
461         return status;
462 }
463
464
465 bool Lexer::Pimpl::eatLine()
466 {
467         buff.clear();
468
469         unsigned char c = '\0';
470         char cc = 0;
471         while (is && c != '\n') {
472                 is.get(cc);
473                 c = cc;
474                 //LYXERR(Debug::LYXLEX, "Lexer::EatLine read char: `" << c << '\'');
475                 if (c != '\r' && is)
476                         buff.push_back(c);
477         }
478
479         if (c == '\n') {
480                 ++lineno;
481                 buff.resize(buff.size() - 1);
482                 status = LEX_DATA;
483                 return true;
484         } else if (buff.length() > 0) { // last line
485                 status = LEX_DATA;
486                 return true;
487         } else {
488                 return false;
489         }
490 }
491
492
493 bool Lexer::Pimpl::nextToken()
494 {
495         if (!pushTok.empty()) {
496                 // There can have been a whole line pushed so
497                 // we extract the first word and leaves the rest
498                 // in pushTok. (Lgb)
499                 if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
500                         buff.clear();
501                         pushTok = split(pushTok, buff, ' ');
502                 } else {
503                         buff = pushTok;
504                         pushTok.clear();
505                 }
506                 status = LEX_TOKEN;
507                 return true;
508         }
509
510         status = 0;
511         while (is && !status) {
512                 unsigned char c = 0;
513                 char cc = 0;
514                 is.get(cc);
515                 c = cc;
516                 if ((c >= ' ' || c == '\t') && is) {
517                         buff.clear();
518
519                         if (c == '\\') { // first char == '\\'
520                                 do {
521                                         buff.push_back(c);
522                                         is.get(cc);
523                                         c = cc;
524                                 } while (c > ' ' && c != '\\' && is);
525                         } else {
526                                 do {
527                                         buff.push_back(c);
528                                         is.get(cc);
529                                         c = cc;
530                                 } while ((c >= ' ' || c == '\t') && c != '\\' && is);
531                         }
532
533                         if (c == '\\')
534                                 is.putback(c); // put it back
535                         status = LEX_TOKEN;
536                 }
537
538                 if (c == '\n')
539                         ++lineno;
540
541         }
542         if (status)
543                 return true;
544
545         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
546         buff.clear();
547         return false;
548 }
549
550
551 bool Lexer::Pimpl::inputAvailable()
552 {
553         return is.good();
554 }
555
556
557 void Lexer::Pimpl::pushToken(string const & pt)
558 {
559         pushTok = pt;
560 }
561
562
563
564
565 //////////////////////////////////////////////////////////////////////
566 //
567 // Lexer
568 //
569 //////////////////////////////////////////////////////////////////////
570
571 Lexer::Lexer()
572         : pimpl_(new Pimpl(0, 0)), lastReadOk_(false)
573 {}
574
575
576 void Lexer::init(LexerKeyword * tab, int num)
577 {
578          pimpl_ = new Pimpl(tab, num);
579 }
580
581
582 Lexer::~Lexer()
583 {
584         delete pimpl_;
585 }
586
587
588 bool Lexer::isOK() const
589 {
590         return pimpl_->inputAvailable();
591 }
592
593
594 void Lexer::setLineNumber(int l)
595 {
596         pimpl_->lineno = l;
597 }
598
599
600 int Lexer::lineNumber() const
601 {
602         return pimpl_->lineno;
603 }
604
605
606 istream & Lexer::getStream()
607 {
608         return pimpl_->is;
609 }
610
611
612 void Lexer::pushTable(LexerKeyword * tab, int num)
613 {
614         pimpl_->pushTable(tab, num);
615 }
616
617
618 void Lexer::popTable()
619 {
620         pimpl_->popTable();
621 }
622
623
624 void Lexer::printTable(ostream & os)
625 {
626         pimpl_->printTable(os);
627 }
628
629
630 void Lexer::printError(string const & message) const
631 {
632         pimpl_->printError(message);
633 }
634
635
636 bool Lexer::setFile(FileName const & filename)
637 {
638         return pimpl_->setFile(filename);
639 }
640
641
642 void Lexer::setStream(istream & i)
643 {
644         pimpl_->setStream(i);
645 }
646
647
648 void Lexer::setCommentChar(char c)
649 {
650         pimpl_->setCommentChar(c);
651 }
652
653
654 int Lexer::lex()
655 {
656         return pimpl_->lex();
657 }
658
659
660 int Lexer::getInteger() const
661 {
662         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
663         if (!lastReadOk_) {
664                 pimpl_->printError("integer token missing");
665                 return -1;
666         }
667
668         if (isStrInt(pimpl_->getString()))
669                 return convert<int>(pimpl_->getString());
670
671         lastReadOk_ = false;
672         pimpl_->printError("Bad integer `$$Token'");
673         return -1;
674 }
675
676
677 double Lexer::getFloat() const
678 {
679         // replace comma with dot in case the file was written with
680         // the wrong locale (should be rare, but is easy enough to
681         // avoid).
682         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
683         if (!lastReadOk_) {
684                 pimpl_->printError("float token missing");
685                 return -1;
686         }
687
688         string const str = subst(pimpl_->getString(), ",", ".");
689         if (isStrDbl(str))
690                 return convert<double>(str);
691
692         lastReadOk_ = false;
693         pimpl_->printError("Bad float `$$Token'");
694         return -1;
695 }
696
697
698 string const Lexer::getString() const
699 {
700         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
701
702         if (lastReadOk_)
703         return pimpl_->getString();
704
705         return string();
706 }
707
708
709 docstring const Lexer::getDocString() const
710 {
711         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
712
713         if (lastReadOk_)
714                 return pimpl_->getDocString();
715
716         return docstring();
717 }
718
719
720 // I would prefer to give a tag number instead of an explicit token
721 // here, but it is not possible because Buffer::readDocument uses
722 // explicit tokens (JMarc)
723 string const Lexer::getLongString(string const & endtoken)
724 {
725         string str;
726         string prefix;
727         bool firstline = true;
728
729         while (pimpl_->is) { //< eatLine only reads from is, not from pushTok
730                 if (!eatLine())
731                         // blank line in the file being read
732                         continue;
733
734                 string const token = trim(getString(), " \t");
735
736                 LYXERR(Debug::PARSER, "LongString: `" << getString() << '\'');
737
738                 // We do a case independent comparison, like searchKeyword does.
739                 if (compare_ascii_no_case(token, endtoken) == 0)
740                         break;
741
742                 string tmpstr = getString();
743                 if (firstline) {
744                         size_t i = tmpstr.find_first_not_of(' ');
745                         if (i != string::npos)
746                                 prefix = tmpstr.substr(0, i);
747                         firstline = false;
748                         LYXERR(Debug::PARSER, "Prefix = `" << prefix << "\'");
749                 }
750
751                 // further lines in long strings may have the same
752                 // whitespace prefix as the first line. Remove it.
753                 if (prefix.length() && prefixIs(tmpstr, prefix))
754                         tmpstr.erase(0, prefix.length() - 1);
755
756                 str += ltrim(tmpstr, "\t") + '\n';
757         }
758
759         if (!pimpl_->is)
760                 printError("Long string not ended by `" + endtoken + '\'');
761
762         return str;
763 }
764
765
766 bool Lexer::getBool() const
767 {
768         string const s = pimpl_->getString();   
769         if (s == "false" || s == "0") {
770                 lastReadOk_ = true;
771                 return false;
772         }
773         if (s == "true" || s == "1") {
774                 lastReadOk_ = true;
775                 return true;
776         }
777         pimpl_->printError("Bad boolean `$$Token'. "
778                                  "Use \"false\" or \"true\"");
779         lastReadOk_ = false;
780         return false;
781 }
782
783
784 bool Lexer::eatLine()
785 {
786         return pimpl_->eatLine();
787 }
788
789
790 bool Lexer::next(bool esc)
791 {
792         return pimpl_->next(esc);
793 }
794
795
796 bool Lexer::nextToken()
797 {
798         return pimpl_->nextToken();
799 }
800
801
802 void Lexer::pushToken(string const & pt)
803 {
804         pimpl_->pushToken(pt);
805 }
806
807
808 Lexer::operator void const *() const
809 {
810         // This behaviour is NOT the same as the streams which would
811         // use fail() here. However, our implementation of getString() et al.
812         // can cause the eof() and fail() bits to be set, even though we
813         // haven't tried to read 'em.
814         return lastReadOk_? this : 0;
815 }
816
817
818 bool Lexer::operator!() const
819 {
820         return !lastReadOk_;
821 }
822
823
824 Lexer & Lexer::operator>>(string & s)
825 {
826         if (isOK()) {
827                 next();
828                 s = getString();
829         } else {
830                 lastReadOk_ = false;
831         }
832         return *this;
833 }
834
835
836 Lexer & Lexer::operator>>(docstring & s)
837 {
838         if (isOK()) {
839                 next();
840                 s = getDocString();
841         } else {
842                 lastReadOk_ = false;
843         }
844         return *this;
845 }
846
847
848 Lexer & Lexer::operator>>(double & s)
849 {
850         if (isOK()) {
851                 next();
852                 s = getFloat();
853         } else {
854                 lastReadOk_ = false;
855         }
856         return *this;
857 }
858
859
860 Lexer & Lexer::operator>>(int & s)
861 {
862         if (isOK()) {
863                 next();
864                 s = getInteger();
865         } else {
866                 lastReadOk_ = false;
867         }
868         return *this;
869 }
870
871
872 Lexer & Lexer::operator>>(unsigned int & s)
873 {
874         if (isOK()) {
875                 next();
876                 s = getInteger();
877         } else {
878                 lastReadOk_ = false;
879         }
880         return *this;
881 }
882
883
884 Lexer & Lexer::operator>>(bool & s)
885 {
886         if (isOK()) {
887                 next();
888                 s = getBool();
889         } else {
890                 lastReadOk_ = false;
891         }
892         return *this;
893 }
894
895
896 Lexer & Lexer::operator>>(char & c)
897 {
898         string s;
899         operator>>(s);
900         if (!s.empty())
901                 c = s[0];
902         return *this;
903 }
904
905
906 // quotes a string, e.g. for use in preferences files or as an argument
907 // of the "log" dialog
908 string Lexer::quoteString(string const & arg)
909 {
910         string res;
911         res += '"';
912         res += subst(subst(arg, "\\", "\\\\"), "\"", "\\\"");
913         res += '"';
914         return res;
915 }
916
917
918 // same for docstring
919 docstring Lexer::quoteString(docstring const & arg)
920 {
921         docstring res;
922         res += '"';
923         res += subst(subst(arg, from_ascii("\\"), from_ascii("\\\\")), 
924                      from_ascii("\""), from_ascii("\\\""));
925         res += '"';
926         return res;
927 }
928
929
930 Lexer & Lexer::operator>>(char const * required)
931 {
932         string token;
933         *this >> token;
934         if (token != required) {
935                 LYXERR0("Missing '" << required << "'-tag in " << pimpl_->context 
936                         << ". Got " << token << " instead. Line: " << lineNumber());
937                 pushToken(token);
938         }
939         return *this;
940 }
941
942
943 bool Lexer::checkFor(char const * required)
944 {
945         string token;
946         *this >> token;
947         if (token == required)
948                 return true;
949         pushToken(token);
950         return false;
951 }
952
953
954 void Lexer::setContext(std::string const & str)
955 {
956         pimpl_->context = str;
957 }
958
959
960 } // namespace lyx