1 #LyX 2.0.0svn created this file. For more info see http://www.lyx.org/
6 \use_default_options true
10 \maintain_unincluded_children false
16 \font_typewriter default
17 \font_default_family default
25 \default_output_format default
27 \bibtex_command default
28 \index_command default
29 \paperfontsize default
41 \paperorientation portrait
50 \paragraph_separation indent
51 \paragraph_indentation default
52 \quotes_language english
55 \paperpagestyle default
56 \tracking_changes false
72 \begin_layout Standard
73 Contained herein are some observations and suggestions about how to write
75 \begin_inset Flex Code
78 \begin_layout Plain Layout
84 routines, including some thoughts about common pitfalls.
91 \begin_layout Standard
92 Conversion and reversion routines will always be defined as functions that
93 take an object of type
94 \begin_inset Flex Code
97 \begin_layout Plain Layout
104 This argument, conventionally called
105 \begin_inset Flex Code
108 \begin_layout Plain Layout
114 , represents the LyX document being converted.
116 \begin_inset Flex Code
119 \begin_layout Plain Layout
125 class is defined in the file
126 \begin_inset Flex Code
129 \begin_layout Plain Layout
135 , and it has several properties and a number of methods.
139 \begin_layout Standard
140 Some of the most important properties are:
143 \begin_layout Description
145 \begin_inset Flex Code
148 \begin_layout Plain Layout
155 \begin_inset Flex Code
158 \begin_layout Plain Layout
165 \begin_inset Flex Code
168 \begin_layout Plain Layout
174 , depending upon the document class
177 \begin_layout Description
178 textclass The layout file for this document, e.g.,
179 \begin_inset Flex Code
182 \begin_layout Plain Layout
191 \begin_layout Description
192 default_layout The default layout style for the class.
193 \begin_inset Newline newline
196 Note that this is all
197 \begin_inset Flex Code
200 \begin_layout Plain Layout
206 knows about the layout.
207 It does not know what paragraph styles are available, for example, let
208 alone what their properties might be.
212 \begin_layout Description
213 encoding The document encoding.
216 \begin_layout Description
217 language The document language.
220 \begin_layout Standard
221 These three represent the content of the document.
225 \begin_layout Description
226 header The document header, meaning the lines that come before
227 \begin_inset Flex Code
230 \begin_layout Plain Layout
242 for the LaTeX preamble.
245 \begin_layout Description
246 preamble The LaTeX preamble.
249 \begin_layout Description
250 body The document body.
253 \begin_layout Standard
254 All three of these are lists of strings.
255 The importance of this point will be discussed later.
258 \begin_layout Standard
259 Important methods include:
262 \begin_layout Description
263 warning Writes its argument to the console as a warning.
264 (Also takes an optional argument, the debug level, which can be used to
265 suppress output below a certain debug level, but this is rarely used.)
268 \begin_layout Description
269 error Writes the warning and exits, unless we are in try_hard mode, which
270 is set with a command-line option.
271 Rarely used in converter code, but I shall mention times it might be used
275 \begin_layout Description
276 set_parameter Sets the value of a header parameter.
277 This needs to be a parameter already present in the header or nothing will
281 \begin_layout Description
282 set_textclass This writes the value of the
283 \begin_inset Flex Code
286 \begin_layout Plain Layout
292 member variable to the header.
293 So, for example, one might have something like this in a reversion routine:
296 \begin_layout LyX-Code
297 if document.textclass = 'fancy_new_class':
300 \begin_layout LyX-Code
301 document.textclass = 'old_class'
304 \begin_layout LyX-Code
308 \begin_layout Description
309 add_module Adds a LyX module to the list of modules to be loaded with the
313 \begin_layout Description
314 get_module_list Returns the list of modules to be loaded.
317 \begin_layout Description
318 set_module_list Takes a list as argument and replaces the existing list
322 \begin_layout Standard
323 There are some other methods, too, such as
324 \begin_inset Flex Code
327 \begin_layout Plain Layout
333 , but those are more for `internal' use.
336 \begin_layout Standard
337 It is extremely important to understand that
338 \begin_inset Flex Code
341 \begin_layout Plain Layout
353 \begin_inset Flex Code
356 \begin_layout Plain Layout
362 represents the content of a LyX file---the header, preamble, and body---as
364 It is critical that one maintain this structure when modifying the document.
365 Since Python is not type-safe, one can easily fail to do so if one is not
366 careful, and this will cause problems.
369 \begin_layout Standard
370 For example, one must absolutely never do anything like this:
373 \begin_layout LyX-Code
387 \begin_layout LyX-Code
392 begin_layout Plain Layout
403 \begin_layout LyX-Code
423 \begin_layout LyX-Code
424 document.body[i:i] = newstuff
427 \begin_layout Standard
428 This is supposed to insert an InsetERT at line i of the document, and in
430 But it has the potential to confuse
431 \begin_inset Flex Code
434 \begin_layout Plain Layout
441 Suppose at some later point in the conversion we want to change
442 \begin_inset Flex Code
445 \begin_layout Plain Layout
448 begin_layout Plain Layout
454 \begin_inset Flex Code
457 \begin_layout Plain Layout
460 begin_layout PlainLayout
466 (In fact, this is actually done.) Then we are going to have code that looks
470 \begin_layout LyX-Code
471 i = find_token(document.body, '
475 begin_layout Plain Layout', i)
478 \begin_layout Standard
479 This will not find the occurence of
480 \begin_inset Flex Code
483 \begin_layout Plain Layout
486 begin_layout Plain Layout
491 that we just inserted.
493 \begin_inset Flex Code
496 \begin_layout Plain Layout
502 looks for things at the beginning of lines, and
503 \begin_inset Flex Code
506 \begin_layout Plain Layout
509 begin_layout Plain Layout
514 is not at the beginning of the long string
515 \begin_inset Flex Code
518 \begin_layout Plain Layout
525 It follows a newline, to be sure, but that is different.
526 So what one should do instead is:
529 \begin_layout LyX-Code
534 begin_inset ERT', 'status collapsed',
537 \begin_layout LyX-Code
542 begin_layout Plain Layout', '', 'I am in ERT',
545 \begin_layout LyX-Code
557 \begin_layout LyX-Code
558 document.body[i:i] = newstuff
561 \begin_layout Standard
562 That inserts a bunch of lines.
565 \begin_layout Section
569 \begin_layout Standard
570 There are two Python modules that provide commonly used functions for parsing
571 the file and for modifying it.
572 The parsing functions are in
573 \begin_inset Flex Code
576 \begin_layout Plain Layout
582 and the modifying functions are in
583 \begin_inset Flex Code
586 \begin_layout Plain Layout
593 Both of these files have extensive documentation at the beginning that
594 lists the functions that are available and explains what they do.
596 \begin_inset Flex Code
599 \begin_layout Plain Layout
605 code should familiarize themselves with these functions.
608 \begin_layout Section
609 Common Code Structures and Pitfalls
612 \begin_layout Standard
613 As said, reversion routines receive an argument of type
614 \begin_inset Flex Code
617 \begin_layout Plain Layout
623 , and they almost always have one of two sorts of structure, depending upon
624 whether it is the header or the body that one is modifying.
628 \begin_layout Standard
629 If it is the header, then the routine can be quite simple, because items
630 usually occur in the header only once.
631 So the structure will typically be:
634 \begin_layout LyX-Code
635 def revert_header_stuff(document):
638 \begin_layout LyX-Code
639 i = find_token(document.header, '
644 \begin_layout LyX-Code
648 \begin_layout LyX-Code
652 \begin_layout LyX-Code
653 document.warning('Hmm')
656 \begin_layout LyX-Code
660 \begin_layout LyX-Code
661 # do something with line i
664 \begin_layout Standard
665 How complex such routines become depends of course on the case.
668 \begin_layout Standard
669 If the changes will be made to the body, then the routine usually has this
673 \begin_layout LyX-Code
674 def revert_something(document):
677 \begin_layout LyX-Code
681 \begin_layout LyX-Code
685 \begin_layout LyX-Code
686 i = find_token(document.body, '
688 begin_inset Funky', i)
691 \begin_layout LyX-Code
695 \begin_layout LyX-Code
699 \begin_layout LyX-Code
703 \begin_layout LyX-Code
704 i += 1 # or other appropriate reset
707 \begin_layout Standard
708 In some cases, one may need both sorts of routines together.
711 \begin_layout Subsection
715 \begin_layout Standard
716 In the course of doing something in this last case, one will often want
717 to look for content in the inset or layout (or whatever) that one has found.
718 Suppose, for example, that one is trying to remove the option
719 \begin_inset Flex Code
722 \begin_layout Plain Layout
729 Then one might think to use code like this in place of the comment.
732 \begin_layout LyX-Code
733 j = find_token(document.body, 'newoption', i)
736 \begin_layout LyX-Code
740 \begin_layout LyX-Code
741 document.warning('UnFunky inset!')
744 \begin_layout LyX-Code
748 \begin_layout LyX-Code
752 \begin_layout Standard
753 This is terrible code, for several reasons.
756 \begin_layout Standard
757 First, it is wrong to break on the error here.
758 The LyX file is corrupted, yes.
759 But that does not necessarily mean that it is unusable---LyX is pretty
760 forgiving---and just because we have failed to find this one option does
761 not mean we should give up.
762 We at least need to try to remove the option from other Funky insets.
763 So the right think to do here is instead:
766 \begin_layout LyX-Code
767 j = find_token(document.body, 'newoption', i)
770 \begin_layout LyX-Code
774 \begin_layout LyX-Code
775 document.warning('UnFunky inset!')
778 \begin_layout LyX-Code
782 \begin_layout LyX-Code
786 \begin_layout LyX-Code
790 \begin_layout --Separator--
794 \begin_layout Standard
795 The second problem is that we have no way of knowing that the line we find
796 here is actually a line containing an option for the Funky inset on line
798 Suppose this inset is missing its
799 \begin_inset Flex Code
802 \begin_layout Plain Layout
809 There might be a later one that has a
810 \begin_inset Flex Code
813 \begin_layout Plain Layout
821 \begin_inset Flex Code
824 \begin_layout Plain Layout
831 \begin_inset Flex Code
834 \begin_layout Plain Layout
841 If we're just removing it, that might not be so bad.
842 But if we were doing something more extensive, it could be.
843 So, at the very least, we need to find the end of this inset and make sure
844 the option comes before that:
847 \begin_layout LyX-Code
848 k = find_end_of_inset(document.body, i)
851 \begin_layout LyX-Code
855 \begin_layout LyX-Code
856 document.warning('No end to Funky inset!')
859 \begin_layout LyX-Code
863 \begin_layout LyX-Code
867 \begin_layout LyX-Code
868 j = find_token(document.body, 'newoption', i, k)
871 \begin_layout LyX-Code
875 \begin_layout LyX-Code
876 document.warning('UnFunky inset!')
879 \begin_layout LyX-Code
883 \begin_layout LyX-Code
887 \begin_layout LyX-Code
891 \begin_layout Standard
892 Note that we can reset
893 \begin_inset Flex Code
896 \begin_layout Plain Layout
903 \begin_inset Flex Code
906 \begin_layout Plain Layout
912 here only if we know that no Funky inset can occur inside a Funky inset.
913 Otherwise, it should have been
914 \begin_inset Flex Code
917 \begin_layout Plain Layout
926 \begin_layout Standard
927 By the way, although it is not often done, there are definitely cases where
929 \begin_inset Flex Code
932 \begin_layout Plain Layout
939 \begin_inset Flex Code
942 \begin_layout Plain Layout
949 In particular, suppose that we are actually planning to remove Funky insets
950 altogether, or to replace them with ERT.
951 Then, if the file is so corrupt that we cannot find the end of the inset,
952 we cannot do this work, so we
956 we cannot produce a LyX file an older version will be able to load.
957 In that case, it seems right just to abort, and if the user wants to
958 \begin_inset Quotes eld
962 \begin_inset Quotes erd
966 \begin_inset Flex Code
969 \begin_layout Plain Layout
975 from the command line and pass the appropriate opttion.
978 \begin_layout Standard
979 The routine above may still fail to do the right thing, however.
981 \begin_inset Flex Code
984 \begin_layout Plain Layout
990 is missing, but, due to a strange typo, one of the lines of text in the
991 inset happens to begin with
992 \begin_inset Quotes eld
996 \begin_inset Quotes erd
1001 \begin_inset Flex Code
1004 \begin_layout Plain Layout
1010 will find that line and we will remove text from the document! This will
1011 not generally happen with command insets, but it can easily happen with
1013 In that case, one has to make sure the option comes before the content
1014 of the inset, and to do that, we must find the first layout in the inset,
1018 \begin_layout LyX-Code
1019 k = find_end_of_inset(document.body, i)
1022 \begin_layout LyX-Code
1026 \begin_layout LyX-Code
1027 document.warning('No end to Funky inset!')
1030 \begin_layout LyX-Code
1034 \begin_layout LyX-Code
1038 \begin_layout LyX-Code
1039 m = find_token(document.body, '
1043 begin_layout', i, k)
1046 \begin_layout LyX-Code
1050 \begin_layout LyX-Code
1051 document.warning('No layout! Hope for the best!')
1054 \begin_layout LyX-Code
1058 \begin_layout LyX-Code
1059 j = find_token(document.body, 'newoption', i, m)
1062 \begin_layout LyX-Code
1066 \begin_layout LyX-Code
1067 document.warning('UnFunky inset!')
1070 \begin_layout LyX-Code
1074 \begin_layout LyX-Code
1078 \begin_layout LyX-Code
1079 del document.body[j]
1082 \begin_layout Standard
1083 Note the response here to
1084 \begin_inset Flex Code
1087 \begin_layout Plain Layout
1094 There is not necessarily a need to give up trying to remove the option.
1095 What the right response is will depend upon the specific case.
1098 \begin_layout Standard
1099 The last problem, though it would be unlikely in this case, is that we might
1101 \begin_inset Flex Code
1104 \begin_layout Plain Layout
1111 \begin_inset Flex Code
1114 \begin_layout Plain Layout
1121 \begin_inset Flex Code
1124 \begin_layout Plain Layout
1130 only looks to see if the beginning of the line matches.
1131 Typically, then, what one really wants is
1132 \begin_inset Flex Code
1135 \begin_layout Plain Layout
1141 , which makes sure that we are finding a complete token.
1145 \begin_layout Plain Layout
1146 In the implementation in LyX 2.0svn and earlier, this function also ignores
1147 other differences in whitespace.
1148 This needs to be fixed and will be once 2.0 is out.
1153 So what we really want, for the entire function, is:
1156 \begin_layout LyX-Code
1157 def revert_something(document):
1160 \begin_layout LyX-Code
1164 \begin_layout LyX-Code
1168 \begin_layout LyX-Code
1169 i = find_token(document.body, '
1171 begin_inset Funky', i)
1174 \begin_layout LyX-Code
1178 \begin_layout LyX-Code
1182 \begin_layout LyX-Code
1183 k = find_end_of_inset(document.body, i)
1186 \begin_layout LyX-Code
1190 \begin_layout LyX-Code
1191 document.warning('No end to Funky inset!')
1194 \begin_layout LyX-Code
1198 \begin_layout LyX-Code
1202 \begin_layout LyX-Code
1203 m = find_token(document.body, '
1207 begin_layout', i, k)
1210 \begin_layout LyX-Code
1214 \begin_layout LyX-Code
1215 document.warning('No layout! Hope for the best!')
1218 \begin_layout LyX-Code
1222 \begin_layout LyX-Code
1223 j = find_token(document.body, 'newoption', i, m)
1226 \begin_layout LyX-Code
1230 \begin_layout LyX-Code
1231 document.warning('UnFunky inset!')
1234 \begin_layout LyX-Code
1238 \begin_layout LyX-Code
1242 \begin_layout LyX-Code
1243 del document.body[j]
1246 \begin_layout LyX-Code
1250 \begin_layout Standard
1251 This is much more complicated than what we had before, but it is much more
1253 (Probably, much of this logic should be wrapped in a function.)
1256 \begin_layout Subsection
1257 Comments and Coding Style
1260 \begin_layout Standard
1261 I've written the previous routine in the style in which most
1262 \begin_inset Flex Code
1265 \begin_layout Plain Layout
1271 routines have generally been written: There are no comments, and all variable
1272 names are completely uninformative.
1273 For all the usual reasons, this is bad.
1274 It will take us a bit of effort to change this practice, but it is worth
1276 The people who have to fix
1277 \begin_inset Flex Code
1280 \begin_layout Plain Layout
1286 bugs are not always the ones who wrote the code, and even the ones who
1287 did may not remember what it was supposed to do.
1288 So let's write something like this:
1291 \begin_layout LyX-Code
1292 def revert_something(document):
1295 \begin_layout LyX-Code
1299 \begin_layout LyX-Code
1303 \begin_layout LyX-Code
1304 i = find_token(document.body, '
1306 begin_inset Funky', i)
1309 \begin_layout LyX-Code
1313 \begin_layout LyX-Code
1317 \begin_layout LyX-Code
1318 endins = find_end_of_inset(document.body, i)
1321 \begin_layout LyX-Code
1325 \begin_layout LyX-Code
1326 document.warning('No end to Funky inset!')
1329 \begin_layout LyX-Code
1333 \begin_layout LyX-Code
1337 \begin_layout LyX-Code
1338 blay = find_token(document.body, '
1342 begin_layout', i, endins)
1345 \begin_layout LyX-Code
1349 \begin_layout LyX-Code
1350 document.warning('No layout! Hope for the best!')
1353 \begin_layout LyX-Code
1357 \begin_layout LyX-Code
1358 optline = find_token(document.body, 'newoption', i, blay)
1361 \begin_layout LyX-Code
1365 \begin_layout LyX-Code
1366 document.warning('UnFunky inset!')
1369 \begin_layout LyX-Code
1373 \begin_layout LyX-Code
1377 \begin_layout LyX-Code
1378 del document.body[optline]
1381 \begin_layout LyX-Code
1385 \begin_layout Standard
1386 No comments really needed in that one, I suppose.
1389 \begin_layout Subsection
1393 \begin_layout Standard
1394 Another common error is relying too much on assumptions about the structure
1395 of a valid LyX file.
1397 Suppose we want to add a
1398 \begin_inset Flex Code
1401 \begin_layout Plain Layout
1409 flag to the first paragraph of any Funky inset.
1410 Then it is tempting to do something like this:
1413 \begin_layout LyX-Code
1414 def add_noindent(document):
1417 \begin_layout LyX-Code
1421 \begin_layout LyX-Code
1425 \begin_layout LyX-Code
1426 i = find_token(document.body, '
1428 begin_inset Funky', i)
1431 \begin_layout LyX-Code
1435 \begin_layout LyX-Code
1439 \begin_layout LyX-Code
1440 document.body.insert(i+4, '
1447 \begin_layout LyX-Code
1451 \begin_layout Standard
1452 Experienced programmers will know that this is bad.
1453 Where does the magic number 4 come from? The answer is that it comes from
1454 examining the LyX file.
1455 One looks at a typical file containing a Funky inset and sees:
1458 \begin_layout LyX-Code
1464 \begin_layout LyX-Code
1468 \begin_layout LyX-Code
1472 \begin_layout LyX-Code
1475 begin_layout Standard
1478 \begin_layout LyX-Code
1479 here is some content
1482 \begin_layout LyX-Code
1488 \begin_layout LyX-Code
1492 \begin_layout LyX-Code
1498 \begin_layout Standard
1500 \begin_inset Flex Code
1503 \begin_layout Plain Layout
1511 goes four lines after the inset, as we might confirm by adding it in LyX
1512 and looking at that file.
1515 \begin_layout Standard
1516 Much of the time, this will work, but there is no guarantee that it will
1517 be correct, and the same goes for any assumption of this sort.
1518 It is not enough even to study the LyX source code and make very sure that
1519 the output routine produces what one thinks it does.
1520 The problem is that the empty line before
1521 \begin_inset Flex Code
1524 \begin_layout Plain Layout
1532 could easily disappear, without any change to the semantics.
1533 Or another line could appear.
1534 There are several reasons for this.
1537 \begin_layout Standard
1538 First, looking at the source code of the current version of LyX tells you
1539 nothing about how the file might have been created by some other version.
1540 Maybe we get tired of blank lines and decide to remove them.
1541 This is not going to be accounted for in some reversion routine in
1542 \begin_inset Flex Code
1545 \begin_layout Plain Layout
1552 The semantics of the file matters, and LyX's ability to read it matters.
1553 Blank lines here and there do not matter.
1556 \begin_layout Standard
1557 Second, LyX files are not always produced by LyX.
1558 Some of them are produced by external scripts (sed, perl, etc) that people
1559 write to do search and replace operations that are not possible inside
1560 LyX (and this will still be true once advanced search and replace is available).
1561 Such files may end up having slightly different structures than are usual,
1562 and yet be perfectly good files.
1565 \begin_layout Standard
1566 Third, and most importantly, the file you are modifying has almost certainly
1567 been through several other conversion routines before it gets to yours.
1568 A quick look at some of these routines will make it very clear how difficult
1569 it is to get all the blank lines in the right places, and people rarely
1570 check for this: They check to make sure the file opens correctly and that
1571 its output is right, but who cares how many blank lines there are? Again,
1572 it is the semantics that matters, not the fine details of file structure.
1575 \begin_layout Standard
1576 Or consider this possibility: Someone else wrote a routine to remove
1577 \begin_inset Flex Code
1580 \begin_layout Plain Layout
1586 , but, since they failed to read this document, their routine has all the
1587 bugs we discussed before.
1589 \begin_inset Flex Code
1592 \begin_layout Plain Layout
1598 is still there in one of the Funky insets in the document, now that it
1599 has gotten to your routine.
1600 So what you actually have is:
1603 \begin_layout LyX-Code
1609 \begin_layout LyX-Code
1613 \begin_layout LyX-Code
1617 \begin_layout LyX-Code
1621 \begin_layout LyX-Code
1624 begin_layout Standard
1627 \begin_layout LyX-Code
1628 here is some content
1631 \begin_layout LyX-Code
1637 \begin_layout LyX-Code
1641 \begin_layout LyX-Code
1647 \begin_layout Standard
1648 This is not a valid LyX document of the format on which you are operating.
1649 But surely you do not really want to produce this:
1652 \begin_layout LyX-Code
1658 \begin_layout LyX-Code
1662 \begin_layout LyX-Code
1666 \begin_layout LyX-Code
1670 \begin_layout LyX-Code
1676 \begin_layout LyX-Code
1679 begin_layout Standard
1682 \begin_layout LyX-Code
1683 here is some content
1686 \begin_layout LyX-Code
1692 \begin_layout LyX-Code
1696 \begin_layout LyX-Code
1702 \begin_layout Standard
1703 If you do, you will have made matters worse, and also failed to unindent
1705 The file will still open, probably, though with warnings.
1709 \begin_layout Standard
1710 But things can (and do) get much worse.
1711 Suppose you had meant, for some reason, to change the layout, whatever
1713 \begin_inset Flex Code
1716 \begin_layout Plain Layout
1726 \begin_layout LyX-Code
1727 def make_funky_plain(document):
1730 \begin_layout LyX-Code
1734 \begin_layout LyX-Code
1738 \begin_layout LyX-Code
1739 i = find_token(document.body, '
1741 begin_inset Funky', i)
1744 \begin_layout LyX-Code
1748 \begin_layout LyX-Code
1752 \begin_layout LyX-Code
1753 document.body[i+4] = '
1755 begin_layout Plain Layout'
1758 \begin_layout LyX-Code
1762 \begin_layout Standard
1763 Now you've produced this:
1766 \begin_layout LyX-Code
1772 \begin_layout LyX-Code
1776 \begin_layout LyX-Code
1780 \begin_layout LyX-Code
1783 begin_layout Plain Layout
1786 \begin_layout LyX-Code
1789 begin_layout Standard
1792 \begin_layout LyX-Code
1793 here is some content
1796 \begin_layout LyX-Code
1802 \begin_layout LyX-Code
1806 \begin_layout LyX-Code
1812 \begin_layout Standard
1813 LyX will abort the parse when it hits
1814 \begin_inset Flex Code
1817 \begin_layout Plain Layout
1825 , complaining about a missing
1826 \begin_inset Flex Code
1829 \begin_layout Plain Layout
1840 \begin_layout Standard
1841 The solution is very simple:
1844 \begin_layout LyX-Code
1845 def make_funky_plain(document):
1848 \begin_layout LyX-Code
1852 \begin_layout LyX-Code
1856 \begin_layout LyX-Code
1857 i = find_token(document.body, '
1859 begin_inset Funky', i)
1862 \begin_layout LyX-Code
1866 \begin_layout LyX-Code
1870 \begin_layout LyX-Code
1871 endins = find_end_of_inset(document.body, i)
1874 \begin_layout LyX-Code
1878 \begin_layout LyX-Code
1882 \begin_layout LyX-Code
1883 lay = find_token(document.body, '
1885 begin_layout', i, endins)
1888 \begin_layout LyX-Code
1892 \begin_layout LyX-Code
1896 \begin_layout LyX-Code
1897 document.body[lay] = '
1899 begin_layout Plain Layout'
1902 \begin_layout LyX-Code
1906 \begin_layout Standard
1907 Again, a bit more complex, but reliable.