summaryrefslogtreecommitdiff
path: root/macros/luatex/optex/base/verbatim.opm
blob: 028f544f7c2853a0325a7f9f3d762bba8ce38c20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
%% This is part of OpTeX project, see http://petr.olsak.net/optex

\_codedecl \begtt {Verbatim <2020-11-13>} % preloaded in format

   \_doc ----------------------------
   The internal parameters 
   \`\_ttskip`, \`\_ttpenalty`, \`\_viline`, \`\_vifile` and \`\_ttfont`
   for verbatim macros are set. 
   \_cod ----------------------------

\_def\_ttskip{\_medskip}           % space above and below \begtt, \verbinput
\_mathchardef\_ttpenalty=100       % penalty between lines in \begtt, \verbinput
\_newcount\_viline                 % last line number in \verbinput
\_newread\_vifile                  % file given by \verinput
\_def\_ttfont{\_tt}                % default tt font

   \_doc ----------------------------
   \`\code``{<text>}` expands to `\detokenize{<text>}` when `\escapechar=-1`. In
   order to do it more robust when it is used in `\write` then it expands as
   noexpanded `\code<space>` (followed by space in its csname). This macro
   does the real work.

   The \`\_printinverbatim``{<text>}` macro is used for `\code{<text>}` printing and for
   \code{`}<text>\code{`} printing. It is defined as `\hbox`, so the in-verbatim <text> 
   will be never broken. But you can re-define this macro.

   When `\code` occurs in PDF outlines then it does the same as `\detokenize`.
   The macro for preparing outlines sets `\escapechar` to $-1$ and uses
   \^`\_regoul` token list before `\edef`.

   The `\code` is not `\proteced` because we want it expands to
   `\unexpanded{\code<space>{<text>}}` in `\write` parameters. This protect the 
   expansions of the `\code` parameter (like `\\`, `\^` etc.).
   \_cod ----------------------------

\_def\_code#1{\_unexpanded\_ea{\_csname _code \_endcsname{#1}}}
\_protected\_sdef{_code }#1{{\_escapechar=-1 \_ttfont \_the\_everyintt \_relax 
   \_ea\_printinverbatim\_ea{\_detokenize{#1}}}}
\_def\_printinverbatim#1{\_leavevmode\_hbox{#1}}

\_regmacro {}{}{\_let\code=\_detokenize \_let\_code=\_detokenize}
\_public \code ;

   \_doc ----------------------------
   The \`\_setverb` macro sets all catcodes to \"verbatim mode". It should be used only 
   in a group, so we prepare a new catcode table with \"verbatim" catcodes and we define
   it as\nl `\_catcodetable`\`\_verbatimcatcodes`. After the group is finished then
   original catcode table is restored.
   \_cod ----------------------------

\_newcatcodetable \_verbatimcatcodes
\_def\_setverb{\_begingroup
   \_def\do##1{\_catcode`##1=12 }
   \_dospecials   
   \_savecatcodetable\_verbatimcatcodes % all characters are normal
   \_endgroup
}
\_setverb
\_def\_setverb{\_catcodetable\_verbatimcatcodes }%

   \_doc ----------------------------
   \`\activettchar``<char>` saves original catcode of previously declared `<char>` (if
   such character was declared) using \`\_savedttchar` and \`\_savedttcharc`
   values. Then new such values are stored. The declared character is activated
   by `\_adef` as a macro (active character) which opens a group, 
   does `\_setverb` and other settings and reads its parameter until second the same 
   character. This is done by the \`\_readverb` macro. Finally it prints scanned
   `<text>` by \^`\_printinverbatim` and closes group. Suppose that `\activettchar"` is
   used. Then the following work is schematically done:
   \begtt
   \_def "{\_begingroup \_setverb ... \_readverb}
   \_def \_readverb #1"{\_printinverbatim{#1}\_endgroup}
   \endtt
   Note that the second occurrence of `"` is not active because `\_setverb`
   deactivates it.
   \_cod ----------------------------

\_def\_activettchar#1{%
   \_ifx\_savedttchar\_undefined\_else \_catcode\_savedttchar=\_savedttcharc \_fi
   \_chardef\_savedttchar=`#1
   \_chardef\_savedttcharc=\_catcode`#1
   \_adef{#1}{\_begingroup \_setverb \_adef{ }{\_dsp}\_ttfont \_the\_everyintt\_relax \_readverb}%
   \_def\_readverb ##1#1{\_printinverbatim{##1}\_endgroup}%
}
\_public \activettchar ;

   \_doc ----------------------------
   \`\begtt` is defined only as public. We don't need private `\_begtt` variant.
   This macro is defined by `\eoldef`, so user can put a parameter at the
   same line where `\begtt` is. This `#1` parameter is used after `\_everytt`
   parameters settings, so user can change them locally.

   The `\begtt` macro opens group, does \^`\_setverb` and another preprocessing, sets
   `\endlinechar` to `^^J` and reads the following text in verbatim mode 
   until \`\endtt` occurs. This scanning is done by \`\_startverb` macro which is
   defined as:
   \begtt \adef/{\bslash}
   \_def\_startverb #1/endtt #2^^J{...}
   \endtt
   We must to ensure that the backslash in `\endtt` has category 12 (this is a
   reason of the `\ea` chain in real code).
   The `#2` is something between `\endtt` and end of the same line and it is simply
   ignored.

   The `\_startverb` puts the scanned data to \`\_prepareverbdata`. It sets the data
   to `\_tmpb` without changes by default, but you should re-define it in order
   to do special changes, if you want. (For example, \^`\hisyntax` redefines
   this macro.) The scanned data have `^^J` at each end of line and all spaces are
   active characters (defined as {\visiblesp`\ `}). 
   Other characters have normal category 11 or 12.  

   When `\_prepareverbdata` finishes then `\_startverb` runs `\_printverb` loop
   over each line of the data and does a final work: last skip plus `\noindent`
   in the next paragraph. 
   \_cod ---------------------------

\_eoldef \begtt#1{\_par \_wipeepar \_setxhsize
   \_vskip\_parskip \_ttskip
   \_begingroup
   \_setverb 
   \_ifnum\_ttline<0 \_let\_printverblinenum=\_relax \_else \_initverblinenum \_fi
   \_adef{ }{\_dsp}\_adef\^^I{\t}\_parindent=\_ttindent \_parskip=0pt
   \_def\t{\_hskip \_dimexpr\_tabspaces em/2\_relax}%
   \_the\_everytt \_relax #1\_relax \_ttfont
   \_def\_testcommentchars##1\_iftrue{\_iffalse}\_let\_hicomments=\_relax
   \_endlinechar=`^^J
   \_startverb
}
\_ea\_def\_ea\_startverb \_ea#\_ea1\_csstring\\endtt#2^^J{%
   \_prepareverbdata\_tmpb{#1^^J}%
   \_ea\_printverb \_tmpb\_end
   \_par
   \_endgroup \_ttskip
   \_isnextchar\_par{}{\_noindent}%
}
\_def\_prepareverbdata#1#2{\_def#1{#2}}

   \_doc
   The \`\_printverb` macro calls \^`\_printverbline``{<line>}` repeatedly
   to each scanned line of verbatim text. The `\_printverb` is used from
   `\begtt...\endtt` and from `\verbinput` too.

   The \^`\_testcommentchars` replaces the following `\_iftrue` to
   `\_iffalse` by default unless the \~`\commentchars` are set. So, main body
   of the loop is written in `\_else` part of the `\_iftrue` condition.
   The \^`\_printverbline``{<line>}` is called here.

   The \`\_printverbline``{<line>}` expects that it starts in vertical mode and it must
   do `\par` in order to return the vertical mode. The \`\_printverblinenum`
   is used here: it does nothing when `\_ttline`\code{<0} else it prints the line
   number using `\_llap`.
   \_cod ----------------------------

\_def\_printverb #1^^J#2{%
   \_ifx\_printverblinenum\_relax \_else \_global\_advance\_ttline by1 \_fi
   \_testcommentchars #1\_relax\_relax\_relax
   \_iftrue
      \_ifx\_end#2 \_printcomments\_fi
   \_else
      \_ifx\_vcomments\_empty\_else  \_printcomments \_def\_vcomments{}\_fi
      \_ifx\_end#2
         \_bgroup \_adef{ }{}\_def\t{}% if the last line is emtpy, we don't print it
         \_ifcat&#1&\_egroup \_else\_egroup \_printverbline{#1}\_fi
      \_else
         \_printverbline{#1}%
      \_fi
   \_fi
   \_ifx\_end#2 \_let\_next=\_relax \_else \_def\_next{\_printverb#2}\_fi
   \_next
}
\_def\_printverbline#1{\_penalty \_ttpenalty 
   \_indent \_printverblinenum \_kern\_ttshift #1\par}
\_def\_initverblinenum{\_tenrm \_thefontscale[700]\_ea\_let\_ea\_sevenrm\_the\_font}
\_def\_printverblinenum{\_llap{\_sevenrm \_the\_ttline\_kern.9em}}

   \_doc ----------------------------
   Macro \`\verbinput` uses a file read previously or opens the given file. Then
   it runs the parameter scanning by \`\_viscanparameter` and \`\_viscanminus`.
   Finally the \`\_doverbinput` is run. At begining of `\_doverbinput`, we have
   `\_viline`= number of lines already read using previous `\verbinput`,
   `\_vinolines`= the number of lines we need to skip and `\_vidolnes`= the
   number of lines we need to print.
   Similar preparation is done as in `\begtt` after the group is opened. Then
   we skip \`\_vinolines` lines in a loop a and we read \`\_vidolines` lines. The
   read data is accumulated into `\_tmpb` macro. The next steps are equal to
   the steps done in \^`\_startverb` macro: data are processed via
   \^`\_prepareverbdata` and printed via \^`\_printverb` loop.
   \_cod \_fin ----------------------

\_def\_verbinput #1(#2) #3 {\_par \_def\_tmpa{#3}%
   \_def\_tmpb{#1}%  cmds used in local group
   \_ifx\_vifilename\_tmpa \_else
      \_openin\_vifile={#3}%
      \_global\_viline=0 \_global\_let\_vifilename=\_tmpa
      \_ifeof\_vifile
         \_opwarning{\_string\verbinput: file "#3" unable to read}
         \_ea\_ea\_ea\_skiptorelax
      \_fi
   \_fi
   \_viscanparameter #2+\_relax
}
\_def\_skiptorelax#1\_relax{}

\_def \_viscanparameter #1+#2\_relax{%
   \_if$#2$\_viscanminus(#1)\_else \_viscanplus(#1+#2)\_fi
}
\_def\_viscanplus(#1+#2+){%
   \_if$#1$\_tmpnum=\_viline
   \_else \_ifnum#1<0 \_tmpnum=\_viline \_advance\_tmpnum by-#1
       \_else \_tmpnum=#1
             \_advance\_tmpnum by-1
             \_ifnum\_tmpnum<0 \_tmpnum=0 \_fi % (0+13) = (1+13)
   \_fi \_fi
   \_edef\_vinolines{\_the\_tmpnum}%
   \_if$#2$\_def\_vidolines{0}\_else\_edef\_vidolines{#2}\_fi
   \_doverbinput
}
\_def\_viscanminus(#1-#2){%
   \_if$#1$\_tmpnum=0
      \_else \_tmpnum=#1 \_advance\_tmpnum by-1 \_fi
   \_ifnum\_tmpnum<0 \_tmpnum=0 \_fi  % (0-13) = (1-13)
   \_edef\_vinolines{\_the\_tmpnum}%
   \_if$#2$\_tmpnum=0
      \_else \_tmpnum=#2 \_advance\_tmpnum by-\_vinolines \_fi
   \_edef\_vidolines{\_the\_tmpnum}%
   \_doverbinput
}
\_def\_doverbinput{%
   \_tmpnum=\_vinolines
   \_advance\_tmpnum by-\_viline
   \_ifnum\_tmpnum<0
      \_openin\_vifile={\_vifilename}%
      \_global\_viline=0
   \_else
      \_edef\_vinolines{\_the\_tmpnum}%
   \_fi
   \_vskip\_parskip \_ttskip \_wipeepar \_setxhsize
   \_begingroup
   \_ifnum\_ttline<-1 \_let\_printverblinenum=\_relax \_else \_initverblinenum \_fi
   \_setverb \_adef{ }{\_dsp}\_adef\^^I{\t}\_parindent=\_ttindent \_parskip=0pt
   \_def\t{\_hskip \_dimexpr\_tabspaces em/2\_relax}%
   \_the\_everytt\_relax \_tmpb\_relax \_ttfont
   \_endlinechar=`^^J \_tmpnum=0
   \_loop \_ifeof\_vifile \_tmpnum=\_vinolines\_space \_fi
         \_ifnum\_tmpnum<\_vinolines\_space
         \_vireadline \_advance\_tmpnum by1 \_repeat      %% skip lines
   \_edef\_ttlinesave{\_ttline=\_the\_ttline}%
   \_ifnum\_ttline=-1 \_ttline=\_viline \_fi
   \_tmpnum=0 \_def\_tmpb{}%
   \_ifnum\_vidolines=0 \_tmpnum=-1 \_fi
   \_ifeof\_vifile \_tmpnum=\_vidolines\_space \_fi
   \_loop \_ifnum\_tmpnum<\_vidolines\_space
            \_vireadline 
            \_ifnum\_vidolines=0 \_else\_advance\_tmpnum by1 \_fi
            \_ifeof\_vifile \_tmpnum=\_vidolines\_space \_else \_visaveline \_fi %% save line
            \_repeat
   \_ea\_prepareverbdata \_ea \_tmpb\_ea{\_tmpb^^J}%
   \_catcode`\ =10 \_catcode`\%=9 % used in \commentchars comments
   \_ea\_printverb \_tmpb\_end
   \_global\_ttlinesave
   \_par
   \_endgroup
   \_ttskip
   \_isnextchar\_par{}{\_noindent}%
}
\_def\_vireadline{\_read\_vifile to \_tmp \_global\_advance\_viline by1 }
\_def\_visaveline{\_ea\_addto\_ea\_tmpb\_ea{\_tmp}}

\_public \verbinput ;

   \_doc -----------------------------
   If the language of your code\label[commentchars]\wlabel{}
   printed by \^`\verbinput` supports the
   format of comments started by two characters from beginning of the
   line then you can set these characters by \^`\commentchars``<first><second>`.
   Such comments are printed in non-verbatim mode without these two
   characters and they look like the verbatim printing is interrupred
   at the places where such comments are.
   See the section~\ref[lua] for good illustration.
   The file `optex.lua` is read by single command
   `\verbinput (4-) optex.lua` here and the `\commentchars --` was set before it.

   If you need to set a special character by \^`\commentchars` then you must
   to set the catcode to 12 (and space to 13). Examples:
   \begtt
   \commentchars //        % C++ comments
   \commentchars --        % Lua comments
   {\catcode`\%=12 \_ea}\commentchars %%                  % TeX comments
   {\catcode`\#=12 \catcode`\ =13 \_ea}\commentchars#{ }  % bash comments
   \endtt

   There is one limitation when \TeX/ interprets the comments declared by
   \^`\commentchars`. Each block of comments are accummulated to one line
   and then it is re-interpreted by \TeX. So, the ends of lines in the
   comments block are lost. You cannot use macros which
   need to scan end of lines, for example `\begtt...\endtt` inside comments
   block does not work. The character `%` is ignored in comments but you can use `\%`
   for printing or `%` alone for de-activating `\_endpar` from empty
   comment lines.

   Implementation: The \`\commentchars``<first><second>` redefines the \`\_testcommentchars`
   used in \^`\_printverb` in order to it removes the following `\_iftrue`
   and returns `\_iftrue` or `\_iffalse` depending on the fact that
   the comment characters are or aren't present at the beginning ot tested line.
   If it is true (`\ifnum` expands to `\ifnum 10>0`) then the rest of the
   line is added to the \`\_vcomments` macro.

   The \`\_hicomments` is `\relax` by default but it is redefined by \^`\commentchars`
   in order to keep no-colorized comments if we need to use feature from
   \^`\commentchars`.

   The accumulated comments are printed whenever the non-comment line
   occurs. This is done by \`\_printcomments` macro.
   You can re-define it, but main idea must be kept: it is printed in the
   group, `\_reloding \_rm` initializes normal font, `\catcodetable0`
   returns to normal catcode table used before `\verbinput` is started, and
   the text accumulated in `\_vcomments` must be printed by
   `\_scantextokens` primitive.
   \_cod -----------------------------

\_def\_vcomments{}
\_let\_hicomments=\_relax

\_def\_commentchars#1#2{%
   \_def\_testcommentchars ##1##2##3\_relax ##4\_iftrue{\_ifnum % not closed in this macro
      \_ifx #1##1\_ifx#2##21\_fi\_fi 0>0
      \_ifx\_relax##3\_relax \_addto\_vcomments{\_endgraf}% empty comment=\enfgraf
      \_else \_addto\_vcomments{##3 }\_fi}%
   \_def\_hicomments{\_replfromto{\b\n#1#2}{^^J}{\w{#1#2####1}^^J}}% used in \hisyntax
}
\_def\_testcommentchars #1\_iftrue{\_iffalse} % default value of \_testcommentchar
\_def\_printcomments{\_ttskip
   {\_catcodetable0 \_reloading \_rm \_everypar={}%
    \_noindent \_ignorespaces \_scantextokens\_ea{\_vcomments}\_par}%
   \_ttskip
}
\_public \commentchars ;

   \_doc -----------------------------
   The \`\visiblesp` sets spaces as visible characters \char9251.
   It redefines the \^`\_dsp`, so it is useful for verbatim modes only.

   The \`\_dsp` is equivalent to {\visiblesp`\ `} primitive.
   It is used in all verbatim environments: spaces are active and defined as
   `\_dsp` here.
   \_cod -----------------------------

\_def \_visiblesp{\_ifx\_initunifonts\_relax \_def\_dsp{\_char9251 }%
                  \_else \_def\_dsp{\_char32 }\_fi}
\_let\_dsp=\  % primitive "direct space"

\_public \visiblesp ;

\_endcode 

\_endinput

History:
2020-11-13 ... \commentchars implemented
2020-04-22 ... \ttshift introduced
2020-04-06 ... \visiblesp added
2020-04-04 ... ^^I activated as \t for multiline verbatim
               \verbinput <cmds> (...) <filename>, <cmds> added.