-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCodeInCards.html
389 lines (339 loc) · 17.4 KB
/
CodeInCards.html
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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CodeInCards - Anki plugin</title>
<link rel="stylesheet" type="text/css" href="anki.css" />
</head>
<body>
<h1>CodeInCards - an Anki plugin to embed Python code in cards/card templates</h1>
<h2>Description</h2>
<!--
Restore "caveats" so you can mention the fact that it'll break if used in
individual fields (not restricted to templates) and with
AnkiOnline/AnkiMini/AnkiSRS iPhone/AnkiDroid/AnkiAnythingButDesktop. This is
really only a plugin for the desktop.
Also breaks search in these circumstances - although it's hard to imagine what
content you'd move completely into a library.
Mention these caveats earlier.
Not a chance in hell of iPhone support, even if kludged for the 'droid.
Not a chance of AnkiOnline support.
This poses us a problem. I want scripting and I want AnkiDroid. Android has
ASE for running Python and many other scripting languages but you can't embed
it, and running ASE concurrently will be slow and complicated (to do the IPC).
So having burned boats by putting execution-dependent content (both Python and
Markdown) into individual cards, what do I do?
One option is to make Markdown the creation format but settle for HTML as the
storage format - convert into HTML as early as possible and edit that HTML if
necessary later on. If so, move execution into formatQA and be done with it?
You can edit cards in AnkiDroid - relevant because Pythonated templates
will fail when it runs the equivalent of formatQA.
Cases where the card dynamically changes are fairly rare and I could surely
live without them with an exported deck.
Another option is to shadow each field with a Pythonated one. Nah, too many
fields.
Another option is to use a scripting language that's so tiny it can be
embedded in both Python and non-Python implementations of Anki - namely Lua.
Kahlua will do it for Java. Catch: I don't see a Python impl! Also bloats
AnkiDroid somewhat. Also AnkiDroid doesn't have a plugins mechanism so you'd
limit your usership somewhat if people had to use yours.
QUEESY - quick and easy execution/editing system. Bundle all three plugins
in one big thing.
Summary of options for CodeInCards:
* Change to Lua; embed Kahlua in AnkiDroid and in the desktop Anki use PyLux or
its ilk or even write a pure Python Lua.
* Export/upload only pure HTML; which means, effectively, at-entry evaluation
(though you could hold off until a save or sync) - which in turn means
sacrificing per-rendering dynamicness.
* Squirrel away the Pythonated version of each field in a comment, or serialise
it into some unused field in a robust format, and allow its edit only in
clients that support it. This would require that other clients preserve it,
and if used in a client that supports editing we'd have to timestamp the
code so we knew there was a conflict between rendered and source versions.
Or perhaps do a cheap checksum of the rendered version and check that, seeing
as cards can be modified in ways that don't change the rendering.
Options are almost identical for SynSugar:
* Include a Markdown implementation in AnkiDroid. Not looked at this.
* Export/upload only pure HTML, making Markdown a format for writing but not
editing. There never was any per-rendering dynamicness in SynSugar.
* Squirrel away MD version of card - as CIC above.
-->
<p>
This <a href="http://ichi2.net/anki/">Anki</a> plugin loads substition
"libraries" of <a href="http://www.python.org/">Python</a> functions and variables from
<span class="code">%pluginsFolder%/CodeInCards/*.py</span>
then provides syntax to let you substitute the results of library calls (or indeed
any other arbitrary Python code) into cards and/or card templates.
</p>
<p>
You won't be able to do much with this plugin without some Python and/or
programming experience, or a geeky friend who's <a href="fix.jpg">very generous with their time</a>
(and writing escapes/libraries for Anki is not a good way to start learning
— see <a href="http://wiki.python.org/moin/BeginnersGuide">the Python Beginners' Guide</a>
for that). On the other hand, if you do have the experience then you have the
full power of Python at your disposal.
</p>
<h2 id="security">Security</h2>
<p style="color:red; font-size:larger">Warning: This plugin is a potential
security hole. Please don't install it until you've read and understood
the explanation below and weighed up the risks.</p>
<p>
Like any feature that provides ways to execute code, installing this plugin
turns every deck into a potential security hole, just as turning on macros
in a spreadsheet program does. If someone/something deliberately or
accidentally causes you to run CodeInCards escapes you didn't write, or even if
you just write buggy escapes, it could lead to you losing data and/or control
of your computer (e.g. getting infected with viruses and trojans).
</p>
<p>
To minimise this risk, CodeInCards will only execute escapes in decks it
trusts. Decks are untrusted by default and use of escapes in an untrusted deck
will generate a warning message instead of running them.</p>
<h2>Installation</h2>
<p>
Download using <a href="http://ichi2.net/anki/wiki/Plugins">Anki's plugin
browser</a> in the normal way; search for "codeincards". You may also want to
look at the <a href="http://github.com/zw/Anki-Plugins/blob/master/CodeInCards/default.py">example library</a>
for inspiration. You can also <a href="http://github.com/zw/Anki-Plugins/raw/master/CodeInCards/default.py">download this library</a>
and save it in the <span class="code">%pluginsFolder%/CodeInCards/</span>
directory if you'd like to try it.
</p>
<h2>Trust</h2>
<p>
To trust a deck use "Tools→Advanced→CodeInCards Deck Trust…".
<span style="color:red;">
You are strongly advised to trust only those decks which you've built yourself,
from scratch, and which are not shared or synchronised online. You have been
warned!</span>
</p>
<p>
Deck trust settings are per installation — a deck trusted by you and
exported/shared will not be trusted by anyone else unless they go through this
step too. Similarly, if someone e-mails you a deck or you download one, you
would have to explicitly trust it for any CodeInCards escapes to run. The
deck's creation time and name are used together as an identifier, so if you use
the OS to copy a deck you already trust and give it the same name then the copy
will be trusted too (but again, only by your installation).
</p>
<h2>Syntax</h2>
<p>
Escapes may be included either encoded as HTML or among the markup (i.e. you
can enter them directly in the browser-editor field, or in the "HTML editor" window).
The following escape formats are supported. The dollar-formats aim for brevity
over flexibility:
</p>
<div style="color:blue; margin-left:3em; font-family:Courier,monospace">
$varName
</div>
<p style="margin-left:6em; max-width:35em; ">
Interpolate string variable "varName" (it must be a string). Subscripting
and dot-whatever isn't supported; use the bracketed format below instead
for that.
</p>
<div style="color:blue; margin-left:3em; font-family:Courier,monospace">
$functionName(…)
</div>
<p style="margin-left:6em; max-width:35em; ">
Invoke <span class="code">functionName</span>
with the supplied args, and interpolate the returned string (it must be a
string), e.g.:
</p>
<div style="margin-left:9em; font-family:Courier,monospace">
$myFunc(myVar, 'a string arg')
</div>
<p style="margin-left:6em; max-width:35em; ">
The args list is considered to stop at the next closing bracket, even if
it occurs in a string or after a nested function call, so these won't work:
</p>
<div style="margin-left:9em; font-family:Courier,monospace">
$wontWork("a (broken) example")<br/>
$alsoWontWork("some text", anotherFunc())
</div>
<p style="margin-left:6em; max-width:35em; ">
…but see <a href="#fieldsubst">Field Substitution</a> below for an exception.
</p>
<p style="margin-left:6em; max-width:35em; ">
If you're intent on sticking with the dollar-format then you can usually
hack around the string case above using Python's <span class="code">u''</span>
string format and the escape <span class="code">\u0029</span>,
or perhaps just the HTML entity <span class="code">&#x0029;</span>:
</p>
<div style="margin-left:9em; font-family:Courier,monospace">
$willWork(u"a \u0028working\u0029 example")<br/>
$willWork("a <span class="code">&#x0028;</span>working<span class="code">&#x0029;</span> example")<br/>
$stillWontWork("some text", anotherFunc())
</div>
<p style="margin-left:6em; max-width:35em; ">
but the function call case still won't work and it might be easier
just to use the bracketed forms below instead in both cases.
</p>
<p style="margin-left:6em; max-width:35em; ">
You can split a function call over multiple lines:
</p>
<div style="margin-left:9em; font-family:Courier,monospace">
$quotation('''<br/>
Many were increasingly of the opinion that they'd all made a big mistake<br/>
in coming down from the trees in the first place. And some said that even<br/>
the trees had been a bad move, and that no one should ever have left the oceans.<br/>
''')
</div>
<p>
The bracketed formats are more flexible but more verbose. They accept
multiple lines and brackets are allowed; the only banned string is
<span class="code">%}</span>.
</p>
<div style="color:blue; margin-left:3em; font-family:Courier,monospace">
{%= <expression> %}
</div>
<p style="margin-left:6em; max-width:35em; ">
Evaluates <expression> (a single expression) and substitutes
the resulting string (it must be a string). For example:
</p>
<div style="margin-left:9em; font-family:Courier,monospace">
{%= str(6 * 9) %}
</div>
<p style="margin-left:6em; max-width:35em; ">
will yield "54" in the card or template (or "42" if you're a Hitchiker's Guide fan). Function
calls also work (brackets and all), e.g.:
</p>
<div style="margin-left:9em; font-family:Courier,monospace">
{%= functionName("a (working) example", someOtherFunc()) %}
</div>
<p style="margin-left:6em; max-width:35em; "></p>
<div style="color:blue; margin-left:3em; font-family:Courier,monospace">
{% <code> %}
</div>
<p style="margin-left:6em">
Executes <code>. Anything printed to <span class="code">sys.stdout</span> gets substituted, e.g.:
</p>
<div style="margin-left:9em; font-family:Courier,monospace">
{%<br/>
if random.randint(0, 1000000) == 42:<br/>
<span style="white-space:pre"> print "The Ultimate Answer!"</span><br/>
%}<br/>
</div>
<p>
Note that in card templates you'll need to use
<span class="code">{%% … %%}</span>
and <span class="code">{%%= … %%}</span> instead
because Anki uses <span class="code">%</span> for
field value substitutions (a poor choice of escape characters on my part).
</p>
<p>
You can type a literal '$' as <span class="code">$$</span>.
</p>
<p>
You can separate a simple variable substitution from the surrounding text
without introducing trailing whitespace by using a trailing exclamation mark.
For instance, if <span class="code">ABOUT</span> was set to
<span class="code">"~"</span> then:
<ul>
<li><span class="code">$ABOUTten</span> would give an error.</li>
<li><span class="code">$ABOUT ten</span> would yield "<span class="code">~ ten</span>".</li>
<li><span class="code">$ABOUT!ten</span> would yield "<span class="code">~ten</span>" which is probably what you want.</li>
</ul>
</p>
<h3 id="fieldsubst">Field substitution</h3>
<p>
When you save this in the card template editor:
</p>
<div style="margin-left:3em; font-family:Courier,monospace">
$myFunc('foo %(fieldName)s bar')
</div>
<p>
…Anki regenerates the HTML for all the cards, which expands
<span class="code">%(fieldName)s</span> and wraps it in some style markup.
By the time the card gets rendered for (p)review and CodeInCards escapes get
interpreted, CodeInCards will see something like this:
</p>
<div style="margin-left:3em; font-family:Courier,monospace">
$myFunc('foo <span id="fm109283749">fieldContents<span> bar')
</div>
<p>
Provided <span class="code">fieldContents</span>
doesn't contain any brackets itself, this works fine — because it happens
before CodeInCards sees it, it's an exception to the rule that brackets aren't
allowed in <span class="code">$fn()</span> escapes. You must take care when
quoting the argument to <span class="code">myFunc()</span> to prevent a
clash with Anki's <span class="code"><span></span> tag attribute.
Triple quotes are most robust; since it's the template editor, you'll only
need to type them once. If your <span class="code">%(string)s</span>
might contain brackets or extreme quotes then you'll have to use either
<code>{% … %}</code> or the following syntax.
</p>
<p>
Instead of <code>%(fieldName)s</code> you can use <code>$f['fieldName']</code>
to access field contents by name (it'll nest in
<code>$myFunc(f['fieldName'], "bar")</code> too), and the rather longer
<code>{%= card.fact.fields[0].value %}</code> to access by number. Bear in
mind that:
</p>
<ul>
<li>you'll bypass Anki's field formatting and get the raw field contents</li>
<li>you'll become even more dependent on CodeInCards</li>
<li>the by-number form won't evaluate escapes inside the field (<code>%(fieldName)s</code> and <code>$f['fieldName']</code> do)</li>
<li>when you rename the field, Anki won't rename all your escapes automatically</li>
</ul>
<p>Personally, only the last point seems bad to me.</p>
<h2>Execution Environment</h2>
<h3 id="escapes">Escapes</h3>
<p>
Code in escapes is executed at the point that the card gets displayed, and also
whenever the card gets displayed in the cards list. If an escape contains code
with errors then the error will get interpolated as a big red HTML-ised string
every time you (p)review the card.
</p>
<p>
The string "<br />" is removed from within code if present before
execution. This allows you to add code in the card editor as well as
card templates but means you can't include a BR in an escape. If
that matters to you, define a variable in your library called "BR" and
use than in place of "<br/>". No other HTML is removed, so be careful
not to style any part of an escape entered in a card editor field, and
note that < and > will cause cryptic problems.
</p>
<h3 id="libraries">Libraries</h3>
<p>
All <span class="code">*.py</span>
files (Python modules) in the <span class="code">%pluginsFolder%/CodeInCards/</span>
directory will be loaded (imported) when Anki starts. Compile errors will be
flagged up with the normal plugin error dialog at this point. You can reload
all libraries using "Tools→Advanced→Reload CodeInCards Libraries".
</p>
<h3 id="symbols">Symbols</h3>
<p>
Libraries should <span class="code">import CodeInCards</span>
at the top (or <span class="code">import CodeInCards as CIC</span>
for convenience). Escapes automatically have the symbol <span class="code">CIC</span>
referring to the module, for brevity.
</p>
<p>
Escapes have access to all globals in your <a href="#libraries">libraries</a>.
The following symbols are provided by <span class="code">CIC</span>:
</p>
<dl style="margin-left:3em;">
<dt class="code">CIC.showHTML</dt>
<dd>If set to <span class="code">True</span> in an escape, show the resulting HTML markup instead of rendering it; this can help when debugging an escape. The scope of this escaping is limited to the card side (or, if using <code>f['fieldName']</code>, to the field) containing the escapes. Note that if other plugins run after CodeInCards (such as SynSugar) then the escaping may affect the changes they make.</dd>
<dt class="code">CIC.QorA</dt>
<dd>'q' or 'a', depending on which side of the card is being rendered. You can also use just <span class="code">QorA</span> in escapes.</dd>
<dt class="code">CIC.card</dt>
<dd>The card being rendered; see the <a href="http://github.com/dae/libanki/blob/master/anki/cards.py">Anki source</a> for structure details. Warning: this may be <span class="code">None</span> when a card summary is rendered in the browser! You can also use just <span class="code">card</span> in escapes.</dd>
<dt class="code">CIC.isSummary</dt>
<dd><span class="code">True</span> if the card side is being rendered in the cards list, <span class="code">False</span> if it's being rendered in a preview or review.</dd>
<dt class="code">CIC.f</dt>
<dd>As mentioned in <a href="#fieldsubst">Field Substitutions</a> above, this provides <code>dict</code>-like by-name access to fields using <code>f['fieldName']</code>. Only simple getting is supported.</dd>
</dl>
<h2>Licence</h2>
<p>
Copyright 2010 Isaac Wilcox. This program is free software: you can
redistribute it and/or modify it under the terms of the
<a href="http://www.gnu.org/licenses/gpl-3.0.html">GNU General Public License version 3</a>
as published by the Free Software Foundation.
</p>
<h2>Support</h2>
<p>
Known issues are at <a href="http://github.com/zw/Anki-Plugins/issues">github</a>.
You can reach the author at [email protected].
</p>
</body>
</html>