]> git.lyx.org Git - lyx.git/blob - src/insets/InsetERT.cpp
Fixup 572b06d6: reduce cache size for breakString
[lyx.git] / src / insets / InsetERT.cpp
1 /**
2  * \file InsetERT.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  * \author Lars Gullik Bjønnes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetERT.h"
15
16 #include "Cursor.h"
17 #include "FuncRequest.h"
18 #include "FuncStatus.h"
19 #include "InsetLayout.h"
20 #include "Language.h"
21 #include "Lexer.h"
22 #include "xml.h"
23 #include "ParagraphParameters.h"
24 #include "Paragraph.h"
25 #include "output_docbook.h"
26
27 #include "support/docstream.h"
28 #include "support/gettext.h"
29 #include "support/lstrings.h"
30 #include "support/TempFile.h"
31 #include "Encoding.h"
32
33 #include <sstream>
34 #include <regex>
35 #include <iostream>
36
37 using namespace std;
38 using namespace lyx::support;
39
40 namespace lyx {
41
42 InsetERT::InsetERT(Buffer * buf, CollapseStatus status)
43         : InsetCollapsible(buf)
44 {
45         status_ = status;
46 }
47
48
49 InsetERT::InsetERT(InsetERT const & old)
50         : InsetCollapsible(old)
51 {}
52
53
54 void InsetERT::write(ostream & os) const
55 {
56         os << "ERT" << "\n";
57         InsetCollapsible::write(os);
58 }
59
60
61 int InsetERT::plaintext(odocstringstream & os,
62         OutputParams const & rp, size_t max_length) const
63 {
64         if (!rp.inIndexEntry)
65                 // do not output TeX code
66                 return 0;
67
68         ParagraphList::const_iterator par = paragraphs().begin();
69         ParagraphList::const_iterator end = paragraphs().end();
70
71         while (par != end && os.str().size() <= max_length) {
72                 pos_type siz = par->size();
73                 for (pos_type i = 0; i < siz; ++i) {
74                         char_type const c = par->getChar(i);
75                         // output the active characters
76                         switch (c) {
77                         case '|':
78                         case '!':
79                         case '@':
80                                 os.put(c);
81                                 break;
82                         default:
83                                 break;
84                         }
85                 }
86                 ++par;
87         }
88         return 0;
89 }
90
91
92 void InsetERT::docbook(XMLStream & xs, OutputParams const & runparams) const
93 {
94         auto const begin = paragraphs().begin();
95         auto par = begin;
96         auto const end = paragraphs().end();
97
98         odocstringstream os; // No need for XML handling here.
99
100         // Recreate the logic of makeParagraph in output_docbook.cpp, but much simplified: never open <para>
101         // in an ERT, use simple line breaks.
102         // New line after each paragraph of the ERT, save the last one.
103         while (true) { // For each paragraph in the ERT...
104         std::vector<docstring> pars_prepend;
105         std::vector<docstring> pars;
106         std::vector<docstring> pars_append;
107         tie(pars_prepend, pars, pars_append) = par->simpleDocBookOnePar(buffer(), runparams, text().outerFont(distance(begin, par)), 0, false, true);
108
109         for (docstring const & parXML : pars_prepend)
110             xs << XMLStream::ESCAPE_NONE << parXML;
111                 auto p = pars.begin();
112                 while (true) { // For each line of this ERT paragraph...
113                         os << *p;
114                         ++p;
115                         if (p != pars.end())
116                                 os << "\n";
117                         else
118                                 break;
119                 }
120         for (docstring const & parXML : pars_append)
121             xs << XMLStream::ESCAPE_NONE << parXML;
122
123                 ++par;
124                 if (par != end)
125                         os << "\n";
126                 else
127                         break;
128         }
129
130 //      // Implement the special case of \and: split the current item.
131 //      if (os.str() == "\\and" || os.str() == "\\and ") {
132 //              auto lay = getLayout();
133 //      }
134
135         // Try to recognise some commands to have a nicer DocBook output.
136         bool output_as_comment = true;
137
138         // First step: some commands have a direct mapping to DocBook, mostly because the mapping is simply text or
139         // an XML entity.
140         // Logic is similar to that of convertLaTeXCommands in BiblioInfo.cpp.
141         // TODO: make the code even more similar by looping over the string and applying all conversions. (What is not
142         //  recognised should simply be put in comments: have a list of elements that are either already recognised or are
143         //  not yet recognised? Global transformations like \string should then come first.)
144         {
145                 docstring os_trimmed = trim(os.str());
146
147                 // Rewrite \"u to \"{u}.
148                 static regex const regNoBraces(R"(^\\\W\w)");
149                 if (regex_search(to_utf8(os_trimmed), regNoBraces)) {
150                         os_trimmed.insert(3, from_ascii("}"));
151                         os_trimmed.insert(2, from_ascii("{"));
152                 }
153
154                 // Rewrite \" u to \"{u}.
155                 static regex const regSpace(R"(^\\\W \w)");
156                 if (regex_search(to_utf8(os_trimmed), regSpace)) {
157                         os_trimmed[2] = '{';
158                         os_trimmed.insert(4, from_ascii("}"));
159                 }
160
161                 // Look into the global table of Unicode characters if there is a match.
162                 bool termination;
163                 docstring rem;
164                 docstring const converted = Encodings::fromLaTeXCommand(os_trimmed,
165                                                                         Encodings::TEXT_CMD, termination, rem);
166                 if (!converted.empty()) {
167                         // Don't output the characters directly, even if the document should be encoded in UTF-8, for editors that
168                         // do not support all these funky characters.
169                         for (const char_type& character : converted) {
170                                 xs << XMLStream::ESCAPE_NONE << from_ascii("&#" + std::to_string(character) + ';');
171                         }
172                         output_as_comment = false;
173                 }
174         }
175
176         // Second step: the command \string can be ignored. If that's the only command in the ERT, then done.
177         // There may be several occurrences. (\string is 7 characters long.)
178         if (os.str().length() >= 7) {
179                 docstring os_str = os.str();
180
181                 while (os_str.length() >= 7) {
182                         auto os_text = os_str.find(from_ascii("\\string"));
183
184                         if (os_text != lyx::docstring::npos && !std::isalpha(static_cast<int>(os_str[os_text + 7]))) {
185                                 os_str = os_str.substr(0, os_text) + os_str.substr(os_text + 7, os_str.length());
186
187                                 if (os_str.find('\\') == std::string::npos) {
188                                         xs << os_str;
189                                         output_as_comment = false;
190                                         break;
191                                 }
192                         } else {
193                                 break;
194                         }
195                 }
196         }
197
198         // Otherwise, output the ERT as a comment with the appropriate escaping if the command is not recognised.
199         if (output_as_comment) {
200                 xs << XMLStream::ESCAPE_NONE << "<!-- ";
201                 xs << XMLStream::ESCAPE_COMMENTS << os.str();
202                 xs << XMLStream::ESCAPE_NONE << " -->";
203         }
204 }
205
206
207 void InsetERT::doDispatch(Cursor & cur, FuncRequest & cmd)
208 {
209         switch (cmd.action()) {
210         case LFUN_INSET_MODIFY:
211                 if (cmd.getArg(0) == "ert") {
212                         cur.recordUndoInset(this);
213                         setStatus(cur, string2params(to_utf8(cmd.argument())));
214                         break;
215                 }
216                 //fall-through
217         default:
218                 InsetCollapsible::doDispatch(cur, cmd);
219                 break;
220         }
221
222 }
223
224
225 bool InsetERT::getStatus(Cursor & cur, FuncRequest const & cmd,
226         FuncStatus & status) const
227 {
228         switch (cmd.action()) {
229         case LFUN_INSET_INSERT:
230                 status.setEnabled(false);
231                 return true;
232         case LFUN_INSET_MODIFY:
233                 if (cmd.getArg(0) == "ert") {
234                         status.setEnabled(true);
235                         return true;
236                 }
237                 //fall through
238
239         default:
240                 return InsetCollapsible::getStatus(cur, cmd, status);
241         }
242 }
243
244
245
246 docstring const InsetERT::buttonLabel(BufferView const & bv) const
247 {
248         // U+1F512 LOCK
249         docstring const locked = tempfile_ ? docstring(1, 0x1F512) : docstring();
250         if (decoration() == InsetDecoration::CLASSIC)
251                 return locked + (isOpen(bv) ? _("ERT") : getNewLabel(_("ERT")));
252         return locked + getNewLabel(_("ERT"));
253 }
254
255
256 InsetCollapsible::CollapseStatus InsetERT::string2params(string const & in)
257 {
258         if (in.empty())
259                 return Collapsed;
260         istringstream data(in);
261         Lexer lex;
262         lex.setStream(data);
263         lex.setContext("InsetERT::string2params");
264         lex >> "ert";
265         int s;
266         lex >> s;
267         return static_cast<CollapseStatus>(s);
268 }
269
270
271 string InsetERT::params2string(CollapseStatus status)
272 {
273         ostringstream data;
274         data << "ert" << ' ' << status;
275         return data.str();
276 }
277
278
279 docstring InsetERT::xhtml(XMLStream &, OutputParams const &) const
280 {
281         return docstring();
282 }
283
284 } // namespace lyx