]> git.lyx.org Git - features.git/blob - src/texstream.cpp
texstream: implement way to terminate a command depending on the context
[features.git] / src / texstream.cpp
1 /**
2  * \file texstream.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Enrico Forestieri
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "texstream.h"
14
15 #include "TexRow.h"
16
17 #include "support/lstrings.h"
18 #include "support/unicode.h"
19
20 #include <algorithm>
21 #include <cerrno>
22 #include <cstdio>
23 #include <cstring>
24 #include <iconv.h>
25 #include <locale>
26
27 using namespace std;
28
29 using lyx::support::contains;
30 using lyx::support::split;
31
32
33 namespace lyx {
34
35
36 otexrowstream::otexrowstream(odocstream & os)
37         : os_(os), texrow_(make_unique<TexRow>())
38 {}
39
40
41 otexrowstream::~otexrowstream() = default;
42
43
44 unique_ptr<TexRow> otexrowstream::releaseTexRow()
45 {
46         auto p = make_unique<TexRow>();
47         swap(texrow_, p);
48         return p;
49 }
50
51
52 void otexrowstream::put(char_type const & c)
53 {
54         os_.put(c);
55         if (c == '\n')
56                 texrow_->newline();
57 }
58
59
60 void otexstream::put(char_type const & c)
61 {
62         bool isprotected = false;
63         if (protectspace_) {
64                 if (!canbreakline_ && c == ' ') {
65                         os() << "{}";
66                         isprotected = true;
67                 }
68                 protectspace_ = false;
69         }
70         if (terminate_command_) {
71                 if ((c == ' ' || c == '\0') && !isprotected)
72                         // A space follows. Terminate with brackets.
73                         os() << "{}";
74                 else if (c != '\\' && c != '{' && c != '}')
75                         // Non-terminating character follows. Terminate with space.
76                         os() << " ";
77                 terminate_command_ = false;
78         }
79         otexrowstream::put(c);
80         lastChar(c);
81 }
82
83
84 size_t otexstringstream::length()
85 {
86         auto pos = ods_.tellp();
87         return (pos >= 0) ? size_t(pos) : 0;
88 }
89
90
91 TexString otexstringstream::release()
92 {
93         TexString ts(ods_.str(), move(texrow()));
94         // reset this
95         texrow() = TexRow();
96         ods_.clear();
97         ods_.str(docstring());
98         return ts;
99 }
100
101
102 BreakLine breakln;
103 SafeBreakLine safebreakln;
104 TerminateCommand termcmd;
105
106
107 otexstream & operator<<(otexstream & ots, BreakLine)
108 {
109         if (ots.canBreakLine()) {
110                 ots.otexrowstream::put('\n');
111                 ots.lastChar('\n');
112         }
113         ots.protectSpace(false);
114         ots.terminateCommand(false);
115         return ots;
116 }
117
118
119 otexstream & operator<<(otexstream & ots, SafeBreakLine)
120 {
121         otexrowstream & otrs = ots;
122         if (ots.canBreakLine()) {
123                 otrs << "%\n";
124                 ots.lastChar('\n');
125         }
126         ots.protectSpace(false);
127         ots.terminateCommand(false);
128         return ots;
129 }
130
131
132 otexstream & operator<<(otexstream & ots, TerminateCommand)
133 {
134         ots.terminateCommand(true);
135         return ots;
136 }
137
138
139 otexrowstream & operator<<(otexrowstream & ots, odocstream_manip pf)
140 {
141         ots.os() << pf;
142         if (pf == static_cast<odocstream_manip>(endl)) {
143                 ots.texrow().newline();
144         }
145         return ots;
146 }
147
148
149 otexstream & operator<<(otexstream & ots, odocstream_manip pf)
150 {
151         otexrowstream & otrs = ots;
152         otrs << pf;
153         if (pf == static_cast<odocstream_manip>(endl)) {
154                 ots.lastChar('\n');
155         }
156         return ots;
157 }
158
159
160 otexrowstream & operator<<(otexrowstream & ots, TexString ts)
161 {
162         ts.validate();
163         ots.os() << move(ts.str);
164         ots.texrow().append(move(ts.texrow));
165         return ots;
166 }
167
168
169 otexstream & operator<<(otexstream & ots, TexString ts)
170 {
171         size_t const len = ts.str.length();
172         // Check whether there is something to output
173         if (len == 0)
174                 return ots;
175
176         otexrowstream & otrs = ots;
177         bool isprotected = false;
178         char const c = ts.str[0];
179         if (ots.protectSpace()) {
180                 if (!ots.canBreakLine() && c == ' ') {
181                         otrs << "{}";
182                         isprotected = true;
183                 }
184                 ots.protectSpace(false);
185         }
186         if (ots.terminateCommand()) {
187                 if ((c == ' ' || c == '\0') && !isprotected)
188                         // A space follows. Terminate with brackets.
189                         otrs << "{}";
190                 else if (c != '\\' && c != '{' && c != '}')
191                         // Non-terminating character follows. Terminate with space.
192                         otrs << " ";
193                 ots.terminateCommand(false);
194         }
195
196         if (len > 1)
197                 ots.canBreakLine(ts.str[len - 2] != '\n');
198         ots.lastChar(ts.str[len - 1]);
199
200         otrs << move(ts);
201         return ots;
202 }
203
204
205 otexrowstream & operator<<(otexrowstream & ots, docstring const & s)
206 {
207         ots.os() << s;
208         ots.texrow().newlines(count(s.begin(), s.end(), '\n'));
209         return ots;
210 }
211
212
213 otexstream & operator<<(otexstream & ots, docstring const & s)
214 {
215         size_t const len = s.length();
216
217         // Check whether there's something to output
218         if (len == 0)
219                 return ots;
220         otexrowstream & otrs = ots;
221         bool isprotected = false;
222         char const c = s[0];
223         if (ots.protectSpace()) {
224                 if (!ots.canBreakLine() && c == ' ') {
225                         otrs << "{}";
226                         isprotected = true;
227                 }
228                 ots.protectSpace(false);
229         }
230         if (ots.terminateCommand()) {
231                 if ((c == ' ' || c == '\0') && !isprotected)
232                         // A space follows. Terminate with brackets.
233                         otrs << "{}";
234                 else if (c != '\\' && c != '{' && c != '}')
235                         // Non-terminating character follows. Terminate with space.
236                         otrs << " ";
237                 ots.terminateCommand(false);
238         }
239
240         if (contains(s, 0xF0000)) {
241                 // Some encoding changes for the underlying stream are embedded
242                 // in the docstring. The encoding names to be used are enclosed
243                 // between the code points 0xF0000 and 0xF0001, the first two
244                 // characters of plane 15, which is a Private Use Area whose
245                 // codepoints don't have any associated glyph.
246                 docstring s1;
247                 docstring s2 = split(s, s1, 0xF0000);
248                 while (true) {
249                         if (!s1.empty())
250                                 otrs << s1;
251                         if (s2.empty())
252                                 break;
253                         docstring enc;
254                         docstring const s3 = split(s2, enc, 0xF0001);
255                         if (!contains(s2, 0xF0001))
256                                 s2 = split(enc, s1, 0xF0000);
257                         else {
258                                 otrs << setEncoding(to_ascii(enc));
259                                 s2 = split(s3, s1, 0xF0000);
260                         }
261                 }
262         } else
263                 otrs << s;
264
265         if (len > 1)
266                 ots.canBreakLine(s[len - 2] != '\n');
267         ots.lastChar(s[len - 1]);
268         return ots;
269 }
270
271
272 otexrowstream & operator<<(otexrowstream & ots, string const & s)
273 {
274         ots << from_utf8(s);
275         return ots;
276 }
277
278
279 otexstream & operator<<(otexstream & ots, string const & s)
280 {
281         ots << from_utf8(s);
282         return ots;
283 }
284
285
286 otexrowstream & operator<<(otexrowstream & ots, char const * s)
287 {
288         ots << from_utf8(s);
289         return ots;
290 }
291
292
293 otexstream & operator<<(otexstream & ots, char const * s)
294 {
295         ots << from_utf8(s);
296         return ots;
297 }
298
299
300 otexrowstream & operator<<(otexrowstream & ots, char c)
301 {
302         ots.put(c);
303         return ots;
304 }
305
306
307 otexstream & operator<<(otexstream & ots, char c)
308 {
309         ots.put(c);
310         return ots;
311 }
312
313
314 template <typename Type>
315 otexrowstream & operator<<(otexrowstream & ots, Type value)
316 {
317         ots.os() << value;
318         return ots;
319 }
320
321 template otexrowstream & operator<< <SetEnc>(otexrowstream & os, SetEnc);
322 template otexrowstream & operator<< <double>(otexrowstream &, double);
323 template otexrowstream & operator<< <int>(otexrowstream &, int);
324 template otexrowstream & operator<< <unsigned int>(otexrowstream &,
325                                                                                                    unsigned int);
326 template otexrowstream & operator<< <unsigned long>(otexrowstream &,
327                                                                                                         unsigned long);
328
329 #ifdef LYX_USE_LONG_LONG
330 template otexrowstream & operator<< <unsigned long long>(otexrowstream &,
331                                                          unsigned long long);
332 #endif
333
334
335 template <typename Type>
336 otexstream & operator<<(otexstream & ots, Type value)
337 {
338         ots.os() << value;
339         ots.lastChar(0);
340         ots.protectSpace(false);
341         ots.terminateCommand(false);
342         return ots;
343 }
344
345 template otexstream & operator<< <SetEnc>(otexstream & os, SetEnc);
346 template otexstream & operator<< <double>(otexstream &, double);
347 template otexstream & operator<< <int>(otexstream &, int);
348 template otexstream & operator<< <unsigned int>(otexstream &, unsigned int);
349 template otexstream & operator<< <unsigned long>(otexstream &, unsigned long);
350 #ifdef LYX_USE_LONG_LONG
351 template otexstream & operator<< <unsigned long long>(otexstream &, unsigned long long);
352 #endif
353
354 }